mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
Compare commits
89 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bea21e73b6 | ||
|
|
e8e76324d9 | ||
|
|
210db5b265 | ||
|
|
13168b6d77 | ||
|
|
4fe3798787 | ||
|
|
0d56c51664 | ||
|
|
390ca65079 | ||
|
|
3dbffa89b1 | ||
|
|
c28c45f493 | ||
|
|
478723c55f | ||
|
|
1dac5a46f5 | ||
|
|
b396e54d4f | ||
|
|
5bccf31bed | ||
|
|
0179aad2b3 | ||
|
|
db1fd1d72a | ||
|
|
1bfc1c0393 | ||
|
|
7b9515ffa3 | ||
|
|
708020551f | ||
|
|
63d4e877fb | ||
|
|
ec6a8dd86f | ||
|
|
2f8c13b696 | ||
|
|
1411d8a173 | ||
|
|
9e35ebed4a | ||
|
|
aff9612d11 | ||
|
|
22f92294ce | ||
|
|
ad8f3db725 | ||
|
|
023b835d16 | ||
|
|
8a5aba2068 | ||
|
|
676cc19b89 | ||
|
|
654d04c265 | ||
|
|
857bab6641 | ||
|
|
027306a258 | ||
|
|
0f10bb29e8 | ||
|
|
4a73544502 | ||
|
|
9ea75ebec7 | ||
|
|
1454539cd3 | ||
|
|
6dfa0da460 | ||
|
|
78b3029665 | ||
|
|
e116712275 | ||
|
|
1c7d1e5cf1 | ||
|
|
684293ddbf | ||
|
|
f9bf8dbe6f | ||
|
|
eef55bb007 | ||
|
|
637b082976 | ||
|
|
6c20fecab0 | ||
|
|
06c0c77673 | ||
|
|
dd007d55fe | ||
|
|
5ff786a23d | ||
|
|
f0efe4aed6 | ||
|
|
d8b6098e30 | ||
|
|
7356c40174 | ||
|
|
de0a899be7 | ||
|
|
0a3014d35e | ||
|
|
628e76668e | ||
|
|
d89508b1b5 | ||
|
|
fd9745ddcd | ||
|
|
bcc40c8d59 | ||
|
|
9fad4161be | ||
|
|
17fe23a3b9 | ||
|
|
e61d7a3aeb | ||
|
|
8d55564a0e | ||
|
|
f74d9c6b9b | ||
|
|
8fe8b4bbba | ||
|
|
7713f7fe74 | ||
|
|
9d0caf6de0 | ||
|
|
2dc5540910 | ||
|
|
f70e71a3d4 | ||
|
|
31c754dab7 | ||
|
|
70fefb5463 | ||
|
|
8853bd5045 | ||
|
|
e63c65b644 | ||
|
|
d97e167f31 | ||
|
|
48539093bf | ||
|
|
1535e95a5f | ||
|
|
70e0562dc9 | ||
|
|
b7baf24e50 | ||
|
|
ee21d3ae43 | ||
|
|
d8ca878cb1 | ||
|
|
fcdbb3ce98 | ||
|
|
fc6a754b38 | ||
|
|
048a1a8265 | ||
|
|
ec188990d6 | ||
|
|
81b1072322 | ||
|
|
87765564f6 | ||
|
|
ed98d9e658 | ||
|
|
b552daa6dd | ||
|
|
7f4b77ddba | ||
|
|
c6ab439efb | ||
|
|
42a2be136f |
@@ -1,6 +1,7 @@
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const execa = require('execa');
|
||||
const inquirer = require('inquirer');
|
||||
const Listr = require('listr');
|
||||
const semver = require('semver');
|
||||
const tc = require('turbocolor');
|
||||
@@ -34,6 +35,34 @@ function projectPath(project) {
|
||||
return path.join(rootDir, project);
|
||||
}
|
||||
|
||||
async function askNpmTag(version) {
|
||||
const prompts = [
|
||||
{
|
||||
type: 'list',
|
||||
name: 'npmTag',
|
||||
message: 'Select npm tag or specify a new tag',
|
||||
choices: ['latest', 'next', 'v4-lts']
|
||||
.concat([
|
||||
new inquirer.Separator(),
|
||||
{
|
||||
name: 'Other (specify)',
|
||||
value: null
|
||||
}
|
||||
])
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: answers => {
|
||||
return `Will publish ${tc.cyan(version)} to ${tc.cyan(answers.npmTag)}. Continue?`;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const { npmTag, confirm } = await inquirer.prompt(prompts);
|
||||
return { npmTag, confirm };
|
||||
}
|
||||
|
||||
function checkGit(tasks) {
|
||||
tasks.push(
|
||||
{
|
||||
@@ -127,29 +156,33 @@ function preparePackage(tasks, package, version, install) {
|
||||
title: `${pkg.name}: lint`,
|
||||
task: () => execa('npm', ['run', 'lint'], { cwd: projectRoot })
|
||||
});
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: update ionic/core dep to ${version}`,
|
||||
task: () => {
|
||||
updateDependency(pkg, '@ionic/core', version);
|
||||
writePkg(package, pkg);
|
||||
}
|
||||
});
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: test`,
|
||||
task: () => execa('npm', ['test'], { cwd: projectRoot })
|
||||
});
|
||||
// Disable tests for publish, these pass locally
|
||||
// projectTasks.push({
|
||||
// title: `${pkg.name}: test`,
|
||||
// task: async () => await execa('npm', ['test'], { cwd: projectRoot })
|
||||
// });
|
||||
}
|
||||
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: build`,
|
||||
task: () => execa('npm', ['run', 'build'], { cwd: projectRoot })
|
||||
});
|
||||
if (package === 'core') {
|
||||
if (package === 'core' || package === 'packages/react') {
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: npm link`,
|
||||
task: () => execa('npm', ['link'], { cwd: projectRoot })
|
||||
});
|
||||
}
|
||||
|
||||
if (version) {
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: update ionic/core dep to ${version}`,
|
||||
task: () => {
|
||||
updateDependency(pkg, '@ionic/core', version);
|
||||
writePkg(package, pkg);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Add project tasks
|
||||
@@ -186,7 +219,7 @@ function prepareDevPackage(tasks, package, version) {
|
||||
task: () => execa('npm', ['run', 'build'], { cwd: projectRoot })
|
||||
});
|
||||
|
||||
if (package === 'core') {
|
||||
if (package === 'core' || package === 'packages/react') {
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: npm link`,
|
||||
task: () => execa('npm', ['link'], { cwd: projectRoot })
|
||||
@@ -278,6 +311,9 @@ function updateDependency(pkg, dependency, version) {
|
||||
if (pkg.devDependencies && pkg.devDependencies[dependency]) {
|
||||
pkg.devDependencies[dependency] = version;
|
||||
}
|
||||
if (pkg.peerDependencies && pkg.peerDependencies[dependency]) {
|
||||
pkg.peerDependencies[dependency] = version;
|
||||
}
|
||||
}
|
||||
|
||||
function isVersionGreater(oldVersion, newVersion) {
|
||||
@@ -297,6 +333,7 @@ function copyCDNLoader(tasks, version) {
|
||||
module.exports = {
|
||||
checkTestDist,
|
||||
checkGit,
|
||||
askNpmTag,
|
||||
isValidVersion,
|
||||
isVersionGreater,
|
||||
copyCDNLoader,
|
||||
|
||||
@@ -13,6 +13,8 @@ const fs = require('fs-extra');
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
const dryRun = process.argv.indexOf('--dry-run') > -1;
|
||||
|
||||
if (!process.env.GH_TOKEN) {
|
||||
throw new Error('env.GH_TOKEN is undefined');
|
||||
}
|
||||
@@ -26,15 +28,31 @@ async function main() {
|
||||
// repo must be clean
|
||||
common.checkGit(tasks);
|
||||
|
||||
// publish each package in NPM
|
||||
common.publishPackages(tasks, common.packages, version);
|
||||
const { npmTag, confirm } = await common.askNpmTag(version);
|
||||
|
||||
// push tag to git remote
|
||||
publishGit(tasks, version, changelog);
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!dryRun) {
|
||||
// publish each package in NPM
|
||||
common.publishPackages(tasks, common.packages, version, npmTag);
|
||||
|
||||
// push tag to git remote
|
||||
publishGit(tasks, version, changelog, npmTag);
|
||||
}
|
||||
|
||||
const listr = new Listr(tasks);
|
||||
await listr.run();
|
||||
console.log(`\nionic ${version} published!! 🎉\n`);
|
||||
|
||||
// Dry run doesn't publish to npm or git
|
||||
if (dryRun) {
|
||||
console.log(`
|
||||
\n${tc.yellow('Did not publish. Remove the "--dry-run" flag to publish:')}\n${tc.green(version)} to ${tc.cyan(npmTag)}\n
|
||||
`);
|
||||
} else {
|
||||
console.log(`\nionic ${version} published to ${npmTag}!! 🎉\n`);
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.log('\n', tc.red(err), '\n');
|
||||
|
||||
155
CHANGELOG.md
155
CHANGELOG.md
@@ -1,6 +1,159 @@
|
||||
## [4.11.13](https://github.com/ionic-team/ionic/compare/v4.11.12...v4.11.13) (2020-10-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** do not set ampm when the column doesn't exist ([#22224](https://github.com/ionic-team/ionic/issues/22224)) ([210db5b](https://github.com/ionic-team/ionic/commit/210db5b265c04dcdc847f173503c5c096fdb3374))
|
||||
|
||||
|
||||
|
||||
## [4.11.12](https://github.com/ionic-team/ionic/compare/v4.11.11...v4.11.12) (2020-09-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** remove the automatic switching from am to pm and vice versa ([#22208](https://github.com/ionic-team/ionic/issues/22208)) ([0d56c51](https://github.com/ionic-team/ionic/commit/0d56c5166497de38df9be432f8c7577b4b6c0e94)), closes [#18924](https://github.com/ionic-team/ionic/issues/18924)
|
||||
|
||||
|
||||
|
||||
## [4.11.11](https://github.com/ionic-team/ionic/compare/v4.11.0...v4.11.11) (2020-09-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** do not reset to am when changing the hour and pm is set ([#22016](https://github.com/ionic-team/ionic/issues/22016)) ([1dac5a4](https://github.com/ionic-team/ionic/commit/1dac5a46f5e5adad9638e4e4e901bae1058c7287)), closes [#19175](https://github.com/ionic-team/ionic/issues/19175) [#19260](https://github.com/ionic-team/ionic/issues/19260) [#20026](https://github.com/ionic-team/ionic/issues/20026) [#16630](https://github.com/ionic-team/ionic/issues/16630)
|
||||
|
||||
|
||||
## [4.11.10](https://github.com/ionic-team/ionic/compare/v4.11.9...v4.11.10) (2020-01-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **input:** revert previous type change ([db1fd1d](https://github.com/ionic-team/ionic/commit/db1fd1d72a8a0ade824ad2309d1adb2953731f37))
|
||||
|
||||
|
||||
|
||||
## [4.11.9](https://github.com/ionic-team/ionic/compare/v4.11.8...v4.11.9) (2020-01-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** updating type of input value to accept numbers, fixes [#20173](https://github.com/ionic-team/ionic/issues/20173) ([#20267](https://github.com/ionic-team/ionic/issues/20267)) ([7080205](https://github.com/ionic-team/ionic/commit/708020551f9c51ca3b32d7b49bf4572db3dda12e))
|
||||
* **react:** adding missing overlay component events, fixes [#19923](https://github.com/ionic-team/ionic/issues/19923) ([#20266](https://github.com/ionic-team/ionic/issues/20266)) ([ec6a8dd](https://github.com/ionic-team/ionic/commit/ec6a8dd86f3854edba367f79a6ebac7d60eed839))
|
||||
* **react:** Don't render overlay children if isOpen is false, fixes [#20225](https://github.com/ionic-team/ionic/issues/20225) ([#20226](https://github.com/ionic-team/ionic/issues/20226)) ([aff9612](https://github.com/ionic-team/ionic/commit/aff9612d1197dca48eab6eff9d749032c380cf82))
|
||||
* **react:** re attach props on update, fixes 20192 ([#20228](https://github.com/ionic-team/ionic/issues/20228)) ([9e35ebe](https://github.com/ionic-team/ionic/commit/9e35ebed4a1590ef2521f5f8c393bdd9dea32a04))
|
||||
* **react:** remove leaving view when routerdirection is back, fixes [#20124](https://github.com/ionic-team/ionic/issues/20124) ([#20268](https://github.com/ionic-team/ionic/issues/20268)) ([63d4e87](https://github.com/ionic-team/ionic/commit/63d4e877fb18c90d70c4cbd5f66ffccb8ee6489c))
|
||||
* **react:** support routes without a path for notfound routes, fixes [#20259](https://github.com/ionic-team/ionic/issues/20259) ([#20261](https://github.com/ionic-team/ionic/issues/20261)) ([2f8c13b](https://github.com/ionic-team/ionic/commit/2f8c13b6960f9bcfb941c36fa6e1742b96f80ba9))
|
||||
* **react:** update icon types to be a string as well, fixes [#20229](https://github.com/ionic-team/ionic/issues/20229) ([#20230](https://github.com/ionic-team/ionic/issues/20230)) ([1411d8a](https://github.com/ionic-team/ionic/commit/1411d8a173bfefd7db5241218fd5641b7e9da823))
|
||||
|
||||
|
||||
|
||||
## [4.11.8](https://github.com/ionic-team/ionic/compare/v4.11.7...v4.11.8) (2020-01-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **react:** add missing react memory router ([8a5aba2](https://github.com/ionic-team/ionic/commit/8a5aba206865ce2af7f8bb85f4e7cd8dec37831d))
|
||||
* **react:** fixing type of icon in ToastOptions, ActionSheetOptions, fixes [#20100](https://github.com/ionic-team/ionic/issues/20100) ([857bab6](https://github.com/ionic-team/ionic/commit/857bab66419a851c6d189cd1456cd67c1c2d934c))
|
||||
* **react:** supporting ios and md props on icons ([#20170](https://github.com/ionic-team/ionic/issues/20170)) ([676cc19](https://github.com/ionic-team/ionic/commit/676cc19b89cd6374346aaac9cc3292872c7148fa))
|
||||
|
||||
|
||||
|
||||
## [4.11.7](https://github.com/ionic-team/ionic/compare/v4.11.6...v4.11.7) (2019-12-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **react:** fire lifecycle events on initial render, fixes [#20071](https://github.com/ionic-team/ionic/issues/20071) ([9ea75eb](https://github.com/ionic-team/ionic/commit/9ea75ebec7b1367fc0e319fe61c1f42516357e10))
|
||||
|
||||
|
||||
|
||||
## [4.11.6](https://github.com/ionic-team/ionic/compare/v4.11.5...v4.11.6) (2019-12-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **react:** don't show back button when not appropriate ([684293d](https://github.com/ionic-team/ionic/commit/684293ddbf1ad4edce590d56f7ff66fcd6c817a5))
|
||||
* **react:** first render performance improvements ([1c7d1e5](https://github.com/ionic-team/ionic/commit/1c7d1e5cf1ad7e53ebbee2566e8fa89f567f7fb5))
|
||||
* **react:** fix refs for controllers, overlays, ionpage, and ionrouteroutlet, fixes [#19924](https://github.com/ionic-team/ionic/issues/19924) ([#20012](https://github.com/ionic-team/ionic/issues/20012)) ([eef55bb](https://github.com/ionic-team/ionic/commit/eef55bb0072a9e54b1fd7d1c8c69e7fd43b2a5c5))
|
||||
* **react:** support for 'root' router direction, fixes [#19982](https://github.com/ionic-team/ionic/issues/19982) ([#20052](https://github.com/ionic-team/ionic/issues/20052)) ([e116712](https://github.com/ionic-team/ionic/commit/e1167122758b23221935e897bcd65839b75c59aa))
|
||||
* **react:** support navigating to same page and route updates in IonRouterOutlet, fixes [#19891](https://github.com/ionic-team/ionic/issues/19891), [#19892](https://github.com/ionic-team/ionic/issues/19892), [#19986](https://github.com/ionic-team/ionic/issues/19986) ([f9bf8db](https://github.com/ionic-team/ionic/commit/f9bf8dbe6f952ee53b6b213a4c0d043d25f49b93))
|
||||
|
||||
### Upgrade Note
|
||||
|
||||
If you run into a "Property 'translate' is missing in type" error building after updating to 4.11.6, update your React Typings library to the latest:
|
||||
|
||||
npm i @types/react@latest @types/react-dom@latest
|
||||
|
||||
## [4.11.5](https://github.com/ionic-team/ionic/compare/v4.11.0...v4.11.5) (2019-11-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **react:** improved lifecycle hooks to deal with stale closures, fixes [#19873](https://github.com/ionic-team/ionic/issues/19873) ([#19874](https://github.com/ionic-team/ionic/issues/19874)) ([5ff786a](https://github.com/ionic-team/ionic/commit/5ff786a23d5aa32281bbf5daaa7f8156de39caca))
|
||||
|
||||
|
||||
## [4.11.4](https://github.com/ionic-team/ionic/compare/v4.11.1...v4.11.4) (2019-11-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **react:** check for component unmount, fixes [#19859](https://github.com/ionic-team/ionic/issues/19859) ([7356c40](https://github.com/ionic-team/ionic/commit/7356c401742ce2b3241d6ab05fce0fa65d2f1f8a))
|
||||
* **react:** adding multiple subscriptions to lifecycle events, fixes [#19792](https://github.com/ionic-team/ionic/issues/19792) ([#19858](https://github.com/ionic-team/ionic/issues/19858)) ([0a3014d](https://github.com/ionic-team/ionic/commit/0a3014d35e2102570fd3d8c5ada29eb01aab18e9))
|
||||
* **react:** add check to warn if no ionpage is found, fixes [#19832](https://github.com/ionic-team/ionic/issues/19832) ([#19857](https://github.com/ionic-team/ionic/issues/19857)) ([628e766](https://github.com/ionic-team/ionic/commit/628e76668ea72baebdb02b9dcfe24c0da837fb08))
|
||||
* **react:** expand the location stack to better support back button, fixes [#19748](https://github.com/ionic-team/ionic/issues/19748) ([#19856](https://github.com/ionic-team/ionic/issues/19856)) ([d89508b](https://github.com/ionic-team/ionic/commit/d89508b1b58481d518b89362a8792d05f3f451c9))
|
||||
* **react:** adding hardware back button support, fixes(19819) ([#19851](https://github.com/ionic-team/ionic/issues/19851)) ([fd9745d](https://github.com/ionic-team/ionic/commit/fd9745ddcddded76d64220838aef0f599bf4352f))
|
||||
* **react:** adding swipe back functionality and routerOutlet ready improvements, fixes [#19818](https://github.com/ionic-team/ionic/issues/19818) ([#19849](https://github.com/ionic-team/ionic/issues/19849)) ([bcc40c8](https://github.com/ionic-team/ionic/commit/bcc40c8d59b723bbdb1dfd318bfb2219eb8df3cf))
|
||||
* **react:** create a new overlay each time component is presented, fixes [#19841](https://github.com/ionic-team/ionic/issues/19841), [#19823](https://github.com/ionic-team/ionic/issues/19823) ([#19842](https://github.com/ionic-team/ionic/issues/19842)) ([9fad416](https://github.com/ionic-team/ionic/commit/9fad4161be4859969e14d4d33169ef022052d6bf))
|
||||
|
||||
|
||||
## [4.11.3](https://github.com/ionic-team/ionic/compare/v4.11.1...v4.11.3) (2019-10-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **react:** adding change events to iontabs, fixes [#19665](https://github.com/ionic-team/ionic/issues/19665) ([#19711](https://github.com/ionic-team/ionic/issues/19711)) ([b7baf24](https://github.com/ionic-team/ionic/commit/b7baf24e5053a379156e6c3d82c2b5d3afa999f1))
|
||||
* **react:** adding HashRouter to available ion routers, fixes [#19621](https://github.com/ionic-team/ionic/issues/19621) ([#19683](https://github.com/ionic-team/ionic/issues/19683)) ([fcdbb3c](https://github.com/ionic-team/ionic/commit/fcdbb3ce98747d3b37107904ca110daad95e48bc))
|
||||
* **react:** checking if node is actually an element before treating it like one, fixes [#19769](https://github.com/ionic-team/ionic/issues/19769) ([#19783](https://github.com/ionic-team/ionic/issues/19783)) ([9d0caf6](https://github.com/ionic-team/ionic/commit/9d0caf6de070145c4af618847b27e24c49027b8e))
|
||||
* **react:** checking isOpen again after async call before opening overlay, fixes [#19755](https://github.com/ionic-team/ionic/issues/19755) ([f70e71a](https://github.com/ionic-team/ionic/commit/f70e71a3d461cdab65626a5a7e1b6f4d03b852b1))
|
||||
* **react:** don't remove current view, provide a better method to determine showGoBack fixes [#19731](https://github.com/ionic-team/ionic/issues/19731) and [#19732](https://github.com/ionic-team/ionic/issues/19732) ([31c754d](https://github.com/ionic-team/ionic/commit/31c754dab7ada494ff5f0026d5cf3f7f65198eff))
|
||||
* **react:** removing pages from DOM on nav, fixes [#19701](https://github.com/ionic-team/ionic/issues/19701) ([#19712](https://github.com/ionic-team/ionic/issues/19712)) ([ee21d3a](https://github.com/ionic-team/ionic/commit/ee21d3ae43d8c6b076387a58bca655a56c920bcd))
|
||||
* **react:** unmount leaving view when using browser back button, fixes [#19749](https://github.com/ionic-team/ionic/issues/19749) ([#19781](https://github.com/ionic-team/ionic/issues/19781)) ([2dc5540](https://github.com/ionic-team/ionic/commit/2dc554091056612f1bcd2751d6eeb41cae488751))
|
||||
|
||||
|
||||
|
||||
## [4.11.2](https://github.com/ionic-team/ionic/compare/v4.11.0...v4.11.2) (2019-10-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** ensure all elements are cleaned up when calling .destroy() ([#19654](https://github.com/ionic-team/ionic/issues/19654)) ([d97e167](https://github.com/ionic-team/ionic/commit/d97e167))
|
||||
* **header:** collapsible header works in tabs ([#19658](https://github.com/ionic-team/ionic/issues/19658)) ([4853909](https://github.com/ionic-team/ionic/commit/4853909)), closes [#19640](https://github.com/ionic-team/ionic/issues/19640)
|
||||
* **ios:** hide leaving view after nav transition to avoid flicker ([#19691](https://github.com/ionic-team/ionic/issues/19691)) ([70e0562](https://github.com/ionic-team/ionic/commit/70e0562)), closes [#19674](https://github.com/ionic-team/ionic/issues/19674)
|
||||
* **menu:** clamp out of bounds swipe value ([#19684](https://github.com/ionic-team/ionic/issues/19684)) ([1535e95](https://github.com/ionic-team/ionic/commit/1535e95)), closes [#18927](https://github.com/ionic-team/ionic/issues/18927)
|
||||
* **react:** add IonPicker as controller component, fixes [#19620](https://github.com/ionic-team/ionic/issues/19620) ([#19643](https://github.com/ionic-team/ionic/issues/19643)) ([ed98d9e](https://github.com/ionic-team/ionic/commit/ed98d9e))
|
||||
* **react:** adding change events to IonTabs, fixes [#19665](https://github.com/ionic-team/ionic/issues/19665) ([#19711](https://github.com/ionic-team/ionic/issues/19711)) ([b7baf24](https://github.com/ionic-team/ionic/commit/b7baf24))
|
||||
* **react:** adding HashRouter to available ion routers, fixes [#19621](https://github.com/ionic-team/ionic/issues/19621) ([#19683](https://github.com/ionic-team/ionic/issues/19683)) ([fcdbb3c](https://github.com/ionic-team/ionic/commit/fcdbb3c))
|
||||
* **react:** pages no longer hidden when navigating between tabs, fixes [#19646](https://github.com/ionic-team/ionic/issues/19646) ([#19647](https://github.com/ionic-team/ionic/issues/19647)) ([8776556](https://github.com/ionic-team/ionic/commit/8776556))
|
||||
* **react:** ensure views are removed from DOM after navigating back, fixes [#19701](https://github.com/ionic-team/ionic/issues/19701) ([#19712](https://github.com/ionic-team/ionic/issues/19712)) ([ee21d3a](https://github.com/ionic-team/ionic/commit/ee21d3a))
|
||||
|
||||
|
||||
|
||||
## [4.11.1](https://github.com/ionic-team/ionic/compare/v4.11.0...v4.11.1) (2019-10-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **build:** properly update peer dependencies ([#19639](https://github.com/ionic-team/ionic/issues/19639)) ([b552daa](https://github.com/ionic-team/ionic/commit/b552daa))
|
||||
* **react:** add IonPicker as controller component, fixes [#19620](https://github.com/ionic-team/ionic/issues/19620) ([#19643](https://github.com/ionic-team/ionic/issues/19643)) ([ed98d9e](https://github.com/ionic-team/ionic/commit/ed98d9e))
|
||||
* **react:** handle tab back nav better, fixes [#19646](https://github.com/ionic-team/ionic/issues/19646) ([#19647](https://github.com/ionic-team/ionic/issues/19647)) ([8776556](https://github.com/ionic-team/ionic/commit/8776556))
|
||||
* **react:** moving tslint and friends to devDependencies ([#19624](https://github.com/ionic-team/ionic/issues/19624)) ([7f4b77d](https://github.com/ionic-team/ionic/commit/7f4b77d))
|
||||
|
||||
|
||||
|
||||
# [4.11.0 Sodium](https://github.com/ionic-team/ionic/compare/v4.10.3...v4.11.0) (2019-10-09)
|
||||
|
||||
Ionic React! Enjoy! 🧂 🌊 🐼
|
||||
Ionic React! Enjoy! 🧂 🌊 🐼
|
||||
|
||||
## [4.10.3](https://github.com/ionic-team/ionic/compare/v4.10.2...v4.10.3) (2019-10-09)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/angular",
|
||||
"version": "4.11.0",
|
||||
"version": "4.11.13",
|
||||
"description": "Angular specific wrappers for @ionic/core",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -49,7 +49,7 @@
|
||||
"css/"
|
||||
],
|
||||
"dependencies": {
|
||||
"@ionic/core": "4.11.0",
|
||||
"@ionic/core": "4.11.13",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -33,7 +33,7 @@ export class Config {
|
||||
}
|
||||
|
||||
set(key: keyof IonicConfig, value?: any) {
|
||||
console.warn(`[DEPRECATION][Config]: The Config.set() method is deprecated and will be removed in the next major release.`);
|
||||
console.warn(`[DEPRECATION][Config]: The Config.set() method is deprecated and will be removed in Ionic Framework 6.0. Please see https://ionicframework.com/docs/angular/config for alternatives.`);
|
||||
const c = getConfig();
|
||||
if (c) {
|
||||
c.set(key, value);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @ts-check
|
||||
// @ts-check
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
||||
@@ -23,7 +23,7 @@ exports.config = {
|
||||
framework: 'jasmine',
|
||||
jasmineNodeOpts: {
|
||||
showColors: true,
|
||||
defaultTimeoutInterval: 30000,
|
||||
defaultTimeoutInterval: 70000,
|
||||
print: function() {}
|
||||
},
|
||||
onPrepare() {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
package-lock=false
|
||||
@@ -254,7 +254,7 @@ rules:
|
||||
- visibility
|
||||
- z-index
|
||||
|
||||
property-blacklist:
|
||||
property-disallowed-list:
|
||||
- background-position
|
||||
- right
|
||||
- left
|
||||
|
||||
10028
core/package-lock.json
generated
Normal file
10028
core/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/core",
|
||||
"version": "4.11.0",
|
||||
"version": "4.11.13",
|
||||
"description": "Base components for Ionic",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -36,25 +36,25 @@
|
||||
"devDependencies": {
|
||||
"@stencil/core": "1.6.1",
|
||||
"@stencil/sass": "1.0.1",
|
||||
"@types/jest": "24.0.17",
|
||||
"@types/jest": "^26.0.10",
|
||||
"@types/node": "12.7.1",
|
||||
"@types/puppeteer": "1.19.1",
|
||||
"@types/swiper": "4.4.4",
|
||||
"aws-sdk": "^2.497.0",
|
||||
"aws-sdk": "^2.743.0",
|
||||
"clean-css-cli": "^4.1.11",
|
||||
"domino": "^2.1.3",
|
||||
"fs-extra": "^8.0.1",
|
||||
"jest": "24.8.0",
|
||||
"jest-cli": "24.8.0",
|
||||
"np": "^5.0.3",
|
||||
"jest": "^26.4.2",
|
||||
"jest-cli": "^26.4.2",
|
||||
"np": "^6.5.0",
|
||||
"pixelmatch": "4.0.2",
|
||||
"puppeteer": "1.20.0",
|
||||
"rollup": "1.19.4",
|
||||
"rollup-plugin-node-resolve": "5.2.0",
|
||||
"rollup-plugin-virtual": "^1.0.1",
|
||||
"sass": "^1.22.9",
|
||||
"stylelint": "10.1.0",
|
||||
"stylelint-order": "3.0.1",
|
||||
"stylelint": "^13.7.0",
|
||||
"stylelint-order": "^4.1.0",
|
||||
"swiper": "4.5.1",
|
||||
"tslint": "^5.10.0",
|
||||
"tslint-ionic-rules": "0.0.21",
|
||||
@@ -80,7 +80,7 @@
|
||||
"prerelease": "npm run validate && np prerelease --yolo --any-branch --tag next",
|
||||
"prerender.e2e": "node scripts/testing/prerender.js",
|
||||
"start": "npm run build.css && stencil build --dev --watch --serve",
|
||||
"test": "stencil test --spec --e2e",
|
||||
"test": "stencil test --spec --e2e --max-workers=2",
|
||||
"test.spec": "stencil test --spec",
|
||||
"test.spec.debug": "npx --node-arg=\"--inspect-brk\" stencil test --spec",
|
||||
"test.e2e": "stencil test --e2e",
|
||||
|
||||
@@ -64,7 +64,6 @@
|
||||
}
|
||||
|
||||
:host(.chip-outline) {
|
||||
|
||||
border-color: rgba(0, 0, 0, .32);
|
||||
|
||||
background: transparent;
|
||||
|
||||
@@ -122,24 +122,24 @@
|
||||
.transition-effect {
|
||||
position: absolute;
|
||||
|
||||
/* stylelint-disable property-blacklist */
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
left: -100%;
|
||||
/* stylelint-enable property-blacklist */
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
opacity: 0;
|
||||
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.transition-cover {
|
||||
position: absolute;
|
||||
|
||||
/* stylelint-disable property-blacklist */
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
right: 0;
|
||||
/* stylelint-enable property-blacklist */
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -153,9 +153,9 @@
|
||||
display: block;
|
||||
position: absolute;
|
||||
|
||||
/* stylelint-disable property-blacklist */
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
right: 0;
|
||||
/* stylelint-enable property-blacklist */
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
|
||||
width: 10px;
|
||||
height: 100%;
|
||||
|
||||
@@ -220,6 +220,7 @@ describe('datetime-util', () => {
|
||||
"second": undefined,
|
||||
"tzOffset": 0,
|
||||
"year": 1000,
|
||||
"ampm": undefined
|
||||
});
|
||||
});
|
||||
|
||||
@@ -234,6 +235,7 @@ describe('datetime-util', () => {
|
||||
"second": undefined,
|
||||
"tzOffset": 0,
|
||||
"year": undefined,
|
||||
"ampm": undefined
|
||||
});
|
||||
});
|
||||
|
||||
@@ -248,6 +250,7 @@ describe('datetime-util', () => {
|
||||
"second": 20,
|
||||
"tzOffset": 0,
|
||||
"year": 1994,
|
||||
"ampm": undefined
|
||||
});
|
||||
});
|
||||
|
||||
@@ -262,6 +265,7 @@ describe('datetime-util', () => {
|
||||
"second": undefined,
|
||||
"tzOffset": 0,
|
||||
"year": 2018,
|
||||
"ampm": undefined
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -3,10 +3,16 @@
|
||||
* Defaults to the current date if
|
||||
* no date given
|
||||
*/
|
||||
export const getDateValue = (date: DatetimeData, format: string): number => {
|
||||
export const getDateValue = (date: DatetimeData, format: string): number | string => {
|
||||
const getValue = getValueFromFormat(date, format);
|
||||
|
||||
if (getValue !== undefined) { return getValue; }
|
||||
if (getValue !== undefined) {
|
||||
if (format === FORMAT_A || format === FORMAT_a) {
|
||||
date.ampm = getValue;
|
||||
}
|
||||
|
||||
return getValue;
|
||||
}
|
||||
|
||||
const defaultDate = parseDate(new Date().toISOString());
|
||||
return getValueFromFormat((defaultDate as DatetimeData), format);
|
||||
@@ -308,11 +314,15 @@ export const updateDate = (existingData: DatetimeData, newData: any): boolean =>
|
||||
}
|
||||
|
||||
} else if ((newData.year || newData.hour || newData.month || newData.day || newData.minute || newData.second)) {
|
||||
// newData is from of a datetime picker's selected values
|
||||
// update the existing DatetimeData data with the new values
|
||||
|
||||
// do some magic for 12-hour values
|
||||
if (newData.ampm && newData.hour) {
|
||||
// newData is from the datetime picker's selected values
|
||||
// update the existing datetimeValue with the new values
|
||||
if (newData.ampm !== undefined && newData.hour !== undefined) {
|
||||
// change the value of the hour based on whether or not it is am or pm
|
||||
// if the meridiem is pm and equal to 12, it remains 12
|
||||
// otherwise we add 12 to the hour value
|
||||
// if the meridiem is am and equal to 12, we change it to 0
|
||||
// otherwise we use its current hour value
|
||||
// for example: 8 pm becomes 20, 12 am becomes 0, 4 am becomes 4
|
||||
newData.hour.value = (newData.ampm.value === 'pm')
|
||||
? (newData.hour.value === 12 ? 12 : newData.hour.value + 12)
|
||||
: (newData.hour.value === 12 ? 0 : newData.hour.value);
|
||||
@@ -335,7 +345,8 @@ export const updateDate = (existingData: DatetimeData, newData: any): boolean =>
|
||||
? (existingData.hour! < 12 ? existingData.hour! + 12 : existingData.hour!)
|
||||
: (existingData.hour! >= 12 ? existingData.hour! - 12 : existingData.hour))
|
||||
};
|
||||
(existingData as any)['hour'] = newData['hour'].value;
|
||||
existingData['hour'] = newData['hour'].value;
|
||||
existingData['ampm'] = newData['ampm'].value;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -537,6 +548,7 @@ export interface DatetimeData {
|
||||
second?: number;
|
||||
millisecond?: number;
|
||||
tzOffset?: number;
|
||||
ampm?: string;
|
||||
}
|
||||
|
||||
export interface LocaleData {
|
||||
|
||||
@@ -270,6 +270,12 @@ export class Datetime implements ComponentInterface {
|
||||
value: colOptions[colSelectedIndex].value
|
||||
};
|
||||
|
||||
if (data.name !== 'ampm' && this.datetimeValue.ampm !== undefined) {
|
||||
changeData['ampm'] = {
|
||||
value: this.datetimeValue.ampm
|
||||
};
|
||||
}
|
||||
|
||||
this.updateDatetimeValue(changeData);
|
||||
picker.columns = this.generateColumns();
|
||||
});
|
||||
|
||||
@@ -31,12 +31,12 @@
|
||||
<ion-label>Default</ion-label>
|
||||
<ion-datetime></ion-datetime>
|
||||
</ion-item>
|
||||
|
||||
|
||||
<ion-item>
|
||||
<ion-label position="floating">Default with floating label</ion-label>
|
||||
<ion-datetime></ion-datetime>
|
||||
</ion-item>
|
||||
|
||||
|
||||
<ion-item>
|
||||
<ion-label position="floating">Placeholder with floating label</ion-label>
|
||||
<ion-datetime placeholder="Select a date"></ion-datetime>
|
||||
@@ -105,8 +105,8 @@
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>HH:mm</ion-label>
|
||||
<ion-datetime display-format="HH:mm"></ion-datetime>
|
||||
<ion-label>HH:mm A</ion-label>
|
||||
<ion-datetime display-format="HH:mm A"></ion-datetime>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
@@ -119,11 +119,25 @@
|
||||
<ion-datetime display-format="h:mm a" value="01:47"></ion-datetime>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>h:mm A</ion-label>
|
||||
<ion-datetime display-format="h:mm A" value="14:23"></ion-datetime>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>hh:mm A (15 min steps)</ion-label>
|
||||
<ion-datetime display-format="h:mm A" minute-values="0,15,30,45"></ion-datetime>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>YYYY MMM DD hh:mm A</ion-label>
|
||||
<ion-datetime
|
||||
id="todaysDate"
|
||||
display-format="YYYY MMM DD hh:mm A"
|
||||
minute-values="00,05,10,15,20,25,30,35,40,45,50,55">
|
||||
</ion-datetime>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Leap years, summer months</ion-label>
|
||||
<ion-datetime id="customYearValues" display-format="MM/YYYY" pickerFormat="MMMM YYYY" month-values="6,7,8"></ion-datetime>
|
||||
@@ -164,6 +178,9 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
var todaysDateDatetime = document.querySelector('#todaysDate');
|
||||
todaysDateDatetime.value = '2020-09-02T17:15:03.488Z';
|
||||
|
||||
var yearValuesArray = [2020, 2016, 2008, 2004, 2000, 1996];
|
||||
var customYearValues = document.getElementById('customYearValues');
|
||||
customYearValues.yearValues = yearValuesArray;
|
||||
|
||||
@@ -30,6 +30,38 @@ describe('Datetime', () => {
|
||||
expect(monthvalue).toEqual(date.getMonth() + 1);
|
||||
expect(yearValue).toEqual(date.getFullYear());
|
||||
});
|
||||
|
||||
it('it should return the date value for a given time', () => {
|
||||
const dateTimeData: DatetimeData = {
|
||||
hour: 2,
|
||||
minute: 23,
|
||||
tzOffset: 0
|
||||
};
|
||||
|
||||
const hourValue = getDateValue(dateTimeData, 'hh');
|
||||
const minuteValue = getDateValue(dateTimeData, 'mm');
|
||||
const ampmValue = getDateValue(dateTimeData, 'A');
|
||||
|
||||
expect(hourValue).toEqual(2);
|
||||
expect(minuteValue).toEqual(23);
|
||||
expect(ampmValue).toEqual("am");
|
||||
});
|
||||
|
||||
it('it should return the date value for a given time after 12', () => {
|
||||
const dateTimeData: DatetimeData = {
|
||||
hour: 16,
|
||||
minute: 47,
|
||||
tzOffset: 0
|
||||
};
|
||||
|
||||
const hourValue = getDateValue(dateTimeData, 'hh');
|
||||
const minuteValue = getDateValue(dateTimeData, 'mm');
|
||||
const ampmValue = getDateValue(dateTimeData, 'a');
|
||||
|
||||
expect(hourValue).toEqual(4);
|
||||
expect(minuteValue).toEqual(47);
|
||||
expect(ampmValue).toEqual("pm");
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLocalDateTime()', () => {
|
||||
|
||||
@@ -57,14 +57,10 @@ export class Header implements ComponentInterface {
|
||||
// Determine if the header can collapse
|
||||
const hasCollapse = this.collapse === 'condense';
|
||||
const canCollapse = (hasCollapse && getIonMode(this) === 'ios') ? hasCollapse : false;
|
||||
|
||||
if (!canCollapse && this.collapsibleHeaderInitialized) {
|
||||
this.destroyCollapsibleHeader();
|
||||
} else if (canCollapse && !this.collapsibleHeaderInitialized) {
|
||||
const tabs = this.el.closest('ion-tabs');
|
||||
const page = this.el.closest('ion-app,ion-page,.ion-page,page-inner');
|
||||
|
||||
const pageEl = (tabs) ? tabs : (page) ? page : null;
|
||||
const pageEl = this.el.closest('ion-app,ion-page,.ion-page,page-inner');
|
||||
const contentEl = (pageEl) ? pageEl.querySelector('ion-content') : null;
|
||||
|
||||
await this.setupCollapsibleHeader(contentEl, pageEl);
|
||||
@@ -101,21 +97,17 @@ export class Header implements ComponentInterface {
|
||||
|
||||
setHeaderActive(mainHeaderIndex, false);
|
||||
|
||||
// TODO: Find a better way to do this
|
||||
let remainingHeight = 0;
|
||||
for (let i = 1; i <= scrollHeaderIndex.toolbars.length - 1; i++) {
|
||||
remainingHeight += scrollHeaderIndex.toolbars[i].el.clientHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle interaction between toolbar collapse and
|
||||
* showing/hiding content in the primary ion-header
|
||||
*/
|
||||
const toolbarIntersection = (ev: any) => { handleToolbarIntersection(ev, mainHeaderIndex, scrollHeaderIndex); };
|
||||
|
||||
readTask(() => {
|
||||
const mainHeaderHeight = mainHeaderIndex.el.clientHeight;
|
||||
this.intersectionObserver = new IntersectionObserver(toolbarIntersection, { threshold: 0.25, rootMargin: `-${mainHeaderHeight}px 0px 0px 0px` });
|
||||
|
||||
/**
|
||||
* Handle interaction between toolbar collapse and
|
||||
* showing/hiding content in the primary ion-header
|
||||
* as well as progressively showing/hiding the main header
|
||||
* border as the top-most toolbar collapses or expands.
|
||||
*/
|
||||
const toolbarIntersection = (ev: any) => { handleToolbarIntersection(ev, mainHeaderIndex, scrollHeaderIndex); };
|
||||
this.intersectionObserver = new IntersectionObserver(toolbarIntersection, { threshold: [0.25, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1], rootMargin: `-${mainHeaderHeight}px 0px 0px 0px` });
|
||||
this.intersectionObserver.observe(scrollHeaderIndex.toolbars[0].el);
|
||||
});
|
||||
|
||||
@@ -124,7 +116,7 @@ export class Header implements ComponentInterface {
|
||||
* showing/hiding border on last toolbar
|
||||
* in primary header
|
||||
*/
|
||||
this.contentScrollCallback = () => { handleContentScroll(this.scrollEl!, mainHeaderIndex, scrollHeaderIndex, remainingHeight); };
|
||||
this.contentScrollCallback = () => { handleContentScroll(this.scrollEl!, scrollHeaderIndex); };
|
||||
this.scrollEl.addEventListener('scroll', this.contentScrollCallback);
|
||||
});
|
||||
|
||||
|
||||
@@ -49,20 +49,13 @@ export const createHeaderIndex = (headerEl: HTMLElement | undefined): HeaderInde
|
||||
} as HeaderIndex;
|
||||
};
|
||||
|
||||
export const handleContentScroll = (scrollEl: HTMLElement, mainHeaderIndex: HeaderIndex, scrollHeaderIndex: HeaderIndex, remainingHeight = 0) => {
|
||||
export const handleContentScroll = (scrollEl: HTMLElement, scrollHeaderIndex: HeaderIndex) => {
|
||||
readTask(() => {
|
||||
const scrollTop = scrollEl.scrollTop;
|
||||
const lastMainToolbar = mainHeaderIndex.toolbars[mainHeaderIndex.toolbars.length - 1];
|
||||
|
||||
const scale = clamp(1, 1 + (-scrollTop / 500), 1.1);
|
||||
|
||||
const borderOpacity = clamp(0, (scrollTop - remainingHeight) / lastMainToolbar.el.clientHeight, 1);
|
||||
const maxOpacity = 1;
|
||||
const scaledOpacity = borderOpacity * maxOpacity;
|
||||
|
||||
writeTask(() => {
|
||||
scaleLargeTitles(scrollHeaderIndex.toolbars, scale);
|
||||
setToolbarBackgroundOpacity(mainHeaderIndex.toolbars[0], (scaledOpacity === 1) ? undefined : scaledOpacity);
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -75,6 +68,13 @@ const setToolbarBackgroundOpacity = (toolbar: ToolbarIndex, opacity: number | un
|
||||
}
|
||||
};
|
||||
|
||||
const handleToolbarBorderIntersection = (ev: any, mainHeaderIndex: HeaderIndex) => {
|
||||
if (!ev[0].isIntersecting) { return; }
|
||||
|
||||
const scale = ((1 - ev[0].intersectionRatio) * 100) / 75;
|
||||
setToolbarBackgroundOpacity(mainHeaderIndex.toolbars[0], (scale === 1) ? undefined : scale);
|
||||
};
|
||||
|
||||
/**
|
||||
* If toolbars are intersecting, hide the scrollable toolbar content
|
||||
* and show the primary toolbar content. If the toolbars are not intersecting,
|
||||
@@ -82,7 +82,10 @@ const setToolbarBackgroundOpacity = (toolbar: ToolbarIndex, opacity: number | un
|
||||
*/
|
||||
export const handleToolbarIntersection = (ev: any, mainHeaderIndex: HeaderIndex, scrollHeaderIndex: HeaderIndex) => {
|
||||
writeTask(() => {
|
||||
handleToolbarBorderIntersection(ev, mainHeaderIndex);
|
||||
|
||||
const event = ev[0];
|
||||
|
||||
const intersection = event.intersectionRect;
|
||||
const intersectionArea = intersection.width * intersection.height;
|
||||
const rootArea = event.rootBounds.width * event.rootBounds.height;
|
||||
@@ -114,6 +117,7 @@ export const handleToolbarIntersection = (ev: any, mainHeaderIndex: HeaderIndex,
|
||||
if (hasValidIntersection) {
|
||||
setHeaderActive(mainHeaderIndex);
|
||||
setHeaderActive(scrollHeaderIndex, false);
|
||||
setToolbarBackgroundOpacity(mainHeaderIndex.toolbars[0], 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -126,7 +130,6 @@ export const setHeaderActive = (headerIndex: HeaderIndex, active = true) => {
|
||||
} else {
|
||||
headerIndex.el.classList.add('header-collapse-condense-inactive');
|
||||
}
|
||||
setToolbarBackgroundOpacity(headerIndex.toolbars[0], (active) ? undefined : 0);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
|
||||
ion-item-options {
|
||||
@include multi-dir() {
|
||||
/* stylelint-disable property-blacklist */
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
top: 0;
|
||||
right: 0;
|
||||
/* stylelint-enable property-blacklist */
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
}
|
||||
|
||||
@include ltr() {
|
||||
@@ -19,10 +19,10 @@ ion-item-options {
|
||||
justify-content: flex-start;
|
||||
|
||||
&:not(.item-options-end) {
|
||||
/* stylelint-disable property-blacklist */
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
right: auto;
|
||||
left: 0;
|
||||
/* stylelint-enable property-blacklist */
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
|
||||
justify-content: flex-end;
|
||||
}
|
||||
@@ -41,10 +41,10 @@ ion-item-options {
|
||||
|
||||
.item-options-start {
|
||||
@include multi-dir() {
|
||||
/* stylelint-disable property-blacklist */
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
right: auto;
|
||||
left: 0;
|
||||
/* stylelint-enable property-blacklist */
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
}
|
||||
|
||||
@include ltr() {
|
||||
|
||||
@@ -32,7 +32,7 @@ ion-item-sliding .item {
|
||||
|
||||
.item-sliding-active-swipe-end .item-options-end .item-option-expandable {
|
||||
@include multi-dir() {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
/* stylelint-disable-next-line property-disallowed-list */
|
||||
padding-left: 100%;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ ion-item-sliding .item {
|
||||
|
||||
.item-sliding-active-swipe-start .item-options-start .item-option-expandable {
|
||||
@include multi-dir() {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
/* stylelint-disable-next-line property-disallowed-list */
|
||||
padding-right: 100%;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
--ion-safe-area-right: 0px;
|
||||
|
||||
@include multi-dir() {
|
||||
/* stylelint-disable property-blacklist */
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
@@ -75,7 +75,7 @@
|
||||
@include multi-dir() {
|
||||
right: 0;
|
||||
left: auto;
|
||||
/* stylelint-enable property-blacklist */
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { getIonMode } from '../../global/ionic-global';
|
||||
import { Gesture, GestureDetail, IonicAnimation, MenuChangeEventDetail, MenuI, Side } from '../../interface';
|
||||
import { Point, getTimeGivenProgression } from '../../utils/animation/cubic-bezier';
|
||||
import { GESTURE_CONTROLLER } from '../../utils/gesture';
|
||||
import { assert, isEndSide as isEnd } from '../../utils/helpers';
|
||||
import { assert, clamp, isEndSide as isEnd } from '../../utils/helpers';
|
||||
import { menuController } from '../../utils/menu-controller';
|
||||
|
||||
@Component({
|
||||
@@ -441,7 +441,7 @@ AFTER:
|
||||
* to the new easing curve, as `stepValue` is going to be given
|
||||
* in terms of a linear curve.
|
||||
*/
|
||||
newStepValue += getTimeGivenProgression(new Point(0, 0), new Point(0.4, 0), new Point(0.6, 1), new Point(1, 1), adjustedStepValue);
|
||||
newStepValue += getTimeGivenProgression(new Point(0, 0), new Point(0.4, 0), new Point(0.6, 1), new Point(1, 1), clamp(0, adjustedStepValue, 1));
|
||||
|
||||
this.animation
|
||||
.easing('cubic-bezier(0.4, 0.0, 0.6, 1)')
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
|
||||
.progress,
|
||||
.progress-buffer-bar {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
/* stylelint-disable-next-line property-disallowed-list */
|
||||
transform-origin: left top;
|
||||
|
||||
transition: transform 150ms linear;
|
||||
@@ -89,12 +89,12 @@
|
||||
// --------------------------------------------------
|
||||
|
||||
.indeterminate-bar-primary {
|
||||
/* stylelint-disable property-blacklist */
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: -145.166611%;
|
||||
/* stylelint-enable property-blacklist */
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
|
||||
animation: primary-indeterminate-translate 2s infinite linear;
|
||||
|
||||
@@ -105,12 +105,12 @@
|
||||
}
|
||||
|
||||
.indeterminate-bar-secondary {
|
||||
/* stylelint-disable property-blacklist */
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: -54.888891%;
|
||||
/* stylelint-enable property-blacklist */
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
|
||||
animation: secondary-indeterminate-translate 2s infinite linear;
|
||||
|
||||
@@ -138,7 +138,7 @@
|
||||
:host(.progress-bar-reversed) {
|
||||
.progress,
|
||||
.progress-buffer-bar {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
/* stylelint-disable-next-line property-disallowed-list */
|
||||
transform-origin: right top;
|
||||
}
|
||||
|
||||
|
||||
@@ -105,12 +105,12 @@
|
||||
@include margin-horizontal(-13px, null);
|
||||
|
||||
@include multi-dir() {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
/* stylelint-disable-next-line property-disallowed-list */
|
||||
border-radius: 50% 50% 50% 0;
|
||||
}
|
||||
|
||||
@include rtl() {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
/* stylelint-disable-next-line property-disallowed-list */
|
||||
left: unset;
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
);
|
||||
|
||||
@include rtl() {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
/* stylelint-disable-next-line property-disallowed-list */
|
||||
left: unset;
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
@include position(calc((var(--height) - var(--bar-height)) / 2), null, null, 0);
|
||||
|
||||
@include rtl() {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
/* stylelint-disable-next-line property-disallowed-list */
|
||||
left: unset;
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
);
|
||||
|
||||
@include rtl() {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
/* stylelint-disable-next-line property-disallowed-list */
|
||||
left: unset;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
cursor: grab;
|
||||
pointer-events: all;
|
||||
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ span {
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
/* stylelint-disable property-blacklist */
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
@keyframes shimmer {
|
||||
0% {
|
||||
background-position: -468px 0
|
||||
@@ -66,4 +66,4 @@ span {
|
||||
background-position: 468px 0
|
||||
}
|
||||
}
|
||||
/* stylelint-enable property-blacklist */
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
|
||||
@@ -1,88 +1,145 @@
|
||||
/**
|
||||
* SSR Window 1.0.1
|
||||
* SSR Window 2.0.0
|
||||
* Better handling for window object in SSR environment
|
||||
* https://github.com/nolimits4web/ssr-window
|
||||
*
|
||||
* Copyright 2018, Vladimir Kharlampidi
|
||||
* Copyright 2020, Vladimir Kharlampidi
|
||||
*
|
||||
* Licensed under MIT
|
||||
*
|
||||
* Released on: July 18, 2018
|
||||
* Released on: May 12, 2020
|
||||
*/
|
||||
var doc = (typeof document === 'undefined') ? {
|
||||
body: {},
|
||||
addEventListener: function addEventListener() {},
|
||||
removeEventListener: function removeEventListener() {},
|
||||
activeElement: {
|
||||
blur: function blur() {},
|
||||
nodeName: '',
|
||||
},
|
||||
querySelector: function querySelector() {
|
||||
return null;
|
||||
},
|
||||
querySelectorAll: function querySelectorAll() {
|
||||
return [];
|
||||
},
|
||||
getElementById: function getElementById() {
|
||||
return null;
|
||||
},
|
||||
createEvent: function createEvent() {
|
||||
return {
|
||||
initEvent: function initEvent() {},
|
||||
};
|
||||
},
|
||||
createElement: function createElement() {
|
||||
return {
|
||||
children: [],
|
||||
childNodes: [],
|
||||
style: {},
|
||||
setAttribute: function setAttribute() {},
|
||||
getElementsByTagName: function getElementsByTagName() {
|
||||
return [];
|
||||
},
|
||||
};
|
||||
},
|
||||
location: { hash: '' },
|
||||
} : document; // eslint-disable-line
|
||||
/* eslint-disable no-param-reassign */
|
||||
function isObject(obj) {
|
||||
return (obj !== null &&
|
||||
typeof obj === 'object' &&
|
||||
'constructor' in obj &&
|
||||
obj.constructor === Object);
|
||||
}
|
||||
function extend(target, src) {
|
||||
if (target === void 0) { target = {}; }
|
||||
if (src === void 0) { src = {}; }
|
||||
Object.keys(src).forEach(function (key) {
|
||||
if (typeof target[key] === 'undefined')
|
||||
target[key] = src[key];
|
||||
else if (isObject(src[key]) &&
|
||||
isObject(target[key]) &&
|
||||
Object.keys(src[key]).length > 0) {
|
||||
extend(target[key], src[key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var win = (typeof window === 'undefined') ? {
|
||||
document: doc,
|
||||
navigator: {
|
||||
userAgent: '',
|
||||
},
|
||||
location: {},
|
||||
history: {},
|
||||
CustomEvent: function CustomEvent() {
|
||||
return this;
|
||||
},
|
||||
addEventListener: function addEventListener() {},
|
||||
removeEventListener: function removeEventListener() {},
|
||||
getComputedStyle: function getComputedStyle() {
|
||||
return {
|
||||
getPropertyValue: function getPropertyValue() {
|
||||
return '';
|
||||
},
|
||||
};
|
||||
},
|
||||
Image: function Image() {},
|
||||
Date: function Date() {},
|
||||
screen: {},
|
||||
setTimeout: function setTimeout() {},
|
||||
clearTimeout: function clearTimeout() {},
|
||||
} : window; // eslint-disable-line
|
||||
var doc = typeof document !== 'undefined' ? document : {};
|
||||
var ssrDocument = {
|
||||
body: {},
|
||||
addEventListener: function () { },
|
||||
removeEventListener: function () { },
|
||||
activeElement: {
|
||||
blur: function () { },
|
||||
nodeName: '',
|
||||
},
|
||||
querySelector: function () {
|
||||
return null;
|
||||
},
|
||||
querySelectorAll: function () {
|
||||
return [];
|
||||
},
|
||||
getElementById: function () {
|
||||
return null;
|
||||
},
|
||||
createEvent: function () {
|
||||
return {
|
||||
initEvent: function () { },
|
||||
};
|
||||
},
|
||||
createElement: function () {
|
||||
return {
|
||||
children: [],
|
||||
childNodes: [],
|
||||
style: {},
|
||||
setAttribute: function () { },
|
||||
getElementsByTagName: function () {
|
||||
return [];
|
||||
},
|
||||
};
|
||||
},
|
||||
createElementNS: function () {
|
||||
return {};
|
||||
},
|
||||
importNode: function () {
|
||||
return null;
|
||||
},
|
||||
location: {
|
||||
hash: '',
|
||||
host: '',
|
||||
hostname: '',
|
||||
href: '',
|
||||
origin: '',
|
||||
pathname: '',
|
||||
protocol: '',
|
||||
search: '',
|
||||
},
|
||||
};
|
||||
extend(doc, ssrDocument);
|
||||
|
||||
var win = typeof window !== 'undefined' ? window : {};
|
||||
var ssrWindow = {
|
||||
document: ssrDocument,
|
||||
navigator: {
|
||||
userAgent: '',
|
||||
},
|
||||
location: {
|
||||
hash: '',
|
||||
host: '',
|
||||
hostname: '',
|
||||
href: '',
|
||||
origin: '',
|
||||
pathname: '',
|
||||
protocol: '',
|
||||
search: '',
|
||||
},
|
||||
history: {
|
||||
replaceState: function () { },
|
||||
pushState: function () { },
|
||||
go: function () { },
|
||||
back: function () { },
|
||||
},
|
||||
CustomEvent: function CustomEvent() {
|
||||
return this;
|
||||
},
|
||||
addEventListener: function () { },
|
||||
removeEventListener: function () { },
|
||||
getComputedStyle: function () {
|
||||
return {
|
||||
getPropertyValue: function () {
|
||||
return '';
|
||||
},
|
||||
};
|
||||
},
|
||||
Image: function () { },
|
||||
Date: function () { },
|
||||
screen: {},
|
||||
setTimeout: function () { },
|
||||
clearTimeout: function () { },
|
||||
matchMedia: function () {
|
||||
return {};
|
||||
},
|
||||
};
|
||||
extend(win, ssrWindow);
|
||||
|
||||
/**
|
||||
* Dom7 2.1.3
|
||||
* Dom7 2.1.5
|
||||
* Minimalistic JavaScript library for DOM manipulation, with a jQuery-compatible API
|
||||
* http://framework7.io/docs/dom.html
|
||||
*
|
||||
* Copyright 2019, Vladimir Kharlampidi
|
||||
* Copyright 2020, Vladimir Kharlampidi
|
||||
* The iDangero.us
|
||||
* http://www.idangero.us/
|
||||
*
|
||||
* Licensed under MIT
|
||||
*
|
||||
* Released on: February 11, 2019
|
||||
* Released on: May 15, 2020
|
||||
*/
|
||||
|
||||
class Dom7 {
|
||||
@@ -764,6 +821,79 @@ function add(...args) {
|
||||
return dom;
|
||||
}
|
||||
|
||||
/**
|
||||
* SSR Window 1.0.1
|
||||
* Better handling for window object in SSR environment
|
||||
* https://github.com/nolimits4web/ssr-window
|
||||
*
|
||||
* Copyright 2018, Vladimir Kharlampidi
|
||||
*
|
||||
* Licensed under MIT
|
||||
*
|
||||
* Released on: July 18, 2018
|
||||
*/
|
||||
var doc$1 = (typeof document === 'undefined') ? {
|
||||
body: {},
|
||||
addEventListener: function addEventListener() {},
|
||||
removeEventListener: function removeEventListener() {},
|
||||
activeElement: {
|
||||
blur: function blur() {},
|
||||
nodeName: '',
|
||||
},
|
||||
querySelector: function querySelector() {
|
||||
return null;
|
||||
},
|
||||
querySelectorAll: function querySelectorAll() {
|
||||
return [];
|
||||
},
|
||||
getElementById: function getElementById() {
|
||||
return null;
|
||||
},
|
||||
createEvent: function createEvent() {
|
||||
return {
|
||||
initEvent: function initEvent() {},
|
||||
};
|
||||
},
|
||||
createElement: function createElement() {
|
||||
return {
|
||||
children: [],
|
||||
childNodes: [],
|
||||
style: {},
|
||||
setAttribute: function setAttribute() {},
|
||||
getElementsByTagName: function getElementsByTagName() {
|
||||
return [];
|
||||
},
|
||||
};
|
||||
},
|
||||
location: { hash: '' },
|
||||
} : document; // eslint-disable-line
|
||||
|
||||
var win$1 = (typeof window === 'undefined') ? {
|
||||
document: doc$1,
|
||||
navigator: {
|
||||
userAgent: '',
|
||||
},
|
||||
location: {},
|
||||
history: {},
|
||||
CustomEvent: function CustomEvent() {
|
||||
return this;
|
||||
},
|
||||
addEventListener: function addEventListener() {},
|
||||
removeEventListener: function removeEventListener() {},
|
||||
getComputedStyle: function getComputedStyle() {
|
||||
return {
|
||||
getPropertyValue: function getPropertyValue() {
|
||||
return '';
|
||||
},
|
||||
};
|
||||
},
|
||||
Image: function Image() {},
|
||||
Date: function Date() {},
|
||||
screen: {},
|
||||
setTimeout: function setTimeout() {},
|
||||
clearTimeout: function clearTimeout() {},
|
||||
} : window; // eslint-disable-line
|
||||
|
||||
/**
|
||||
* Swiper 4.5.1
|
||||
* Most modern mobile touch slider and framework with hardware accelerated transitions
|
||||
@@ -847,16 +977,16 @@ const Utils = {
|
||||
let curTransform;
|
||||
let transformMatrix;
|
||||
|
||||
const curStyle = win.getComputedStyle(el, null);
|
||||
const curStyle = win$1.getComputedStyle(el, null);
|
||||
|
||||
if (win.WebKitCSSMatrix) {
|
||||
if (win$1.WebKitCSSMatrix) {
|
||||
curTransform = curStyle.transform || curStyle.webkitTransform;
|
||||
if (curTransform.split(',').length > 6) {
|
||||
curTransform = curTransform.split(', ').map((a) => a.replace(',', '.')).join(', ');
|
||||
}
|
||||
// Some old versions of Webkit choke when 'none' is passed; pass
|
||||
// empty string instead in this case
|
||||
transformMatrix = new win.WebKitCSSMatrix(curTransform === 'none' ? '' : curTransform);
|
||||
transformMatrix = new win$1.WebKitCSSMatrix(curTransform === 'none' ? '' : curTransform);
|
||||
} else {
|
||||
transformMatrix = curStyle.MozTransform || curStyle.OTransform || curStyle.MsTransform || curStyle.msTransform || curStyle.transform || curStyle.getPropertyValue('transform').replace('translate(', 'matrix(1, 0, 0, 1,');
|
||||
matrix = transformMatrix.toString().split(',');
|
||||
@@ -864,7 +994,7 @@ const Utils = {
|
||||
|
||||
if (axis === 'x') {
|
||||
// Latest Chrome and webkits Fix
|
||||
if (win.WebKitCSSMatrix) curTransform = transformMatrix.m41;
|
||||
if (win$1.WebKitCSSMatrix) curTransform = transformMatrix.m41;
|
||||
// Crazy IE10 Matrix
|
||||
else if (matrix.length === 16) curTransform = parseFloat(matrix[12]);
|
||||
// Normal Browsers
|
||||
@@ -872,7 +1002,7 @@ const Utils = {
|
||||
}
|
||||
if (axis === 'y') {
|
||||
// Latest Chrome and webkits Fix
|
||||
if (win.WebKitCSSMatrix) curTransform = transformMatrix.m42;
|
||||
if (win$1.WebKitCSSMatrix) curTransform = transformMatrix.m42;
|
||||
// Crazy IE10 Matrix
|
||||
else if (matrix.length === 16) curTransform = parseFloat(matrix[13]);
|
||||
// Normal Browsers
|
||||
@@ -882,7 +1012,7 @@ const Utils = {
|
||||
},
|
||||
parseUrlQuery(url) {
|
||||
const query = {};
|
||||
let urlToParse = url || win.location.href;
|
||||
let urlToParse = url || win$1.location.href;
|
||||
let i;
|
||||
let params;
|
||||
let param;
|
||||
@@ -929,20 +1059,20 @@ const Utils = {
|
||||
};
|
||||
|
||||
const Support = (function Support() {
|
||||
const testDiv = doc.createElement('div');
|
||||
const testDiv = doc$1.createElement('div');
|
||||
return {
|
||||
touch: (win.Modernizr && win.Modernizr.touch === true) || (function checkTouch() {
|
||||
return !!((win.navigator.maxTouchPoints > 0) || ('ontouchstart' in win) || (win.DocumentTouch && doc instanceof win.DocumentTouch));
|
||||
touch: (win$1.Modernizr && win$1.Modernizr.touch === true) || (function checkTouch() {
|
||||
return !!((win$1.navigator.maxTouchPoints > 0) || ('ontouchstart' in win$1) || (win$1.DocumentTouch && doc$1 instanceof win$1.DocumentTouch));
|
||||
}()),
|
||||
|
||||
pointerEvents: !!(win.navigator.pointerEnabled || win.PointerEvent || ('maxTouchPoints' in win.navigator && win.navigator.maxTouchPoints > 0)),
|
||||
prefixedPointerEvents: !!win.navigator.msPointerEnabled,
|
||||
pointerEvents: !!(win$1.navigator.pointerEnabled || win$1.PointerEvent || ('maxTouchPoints' in win$1.navigator && win$1.navigator.maxTouchPoints > 0)),
|
||||
prefixedPointerEvents: !!win$1.navigator.msPointerEnabled,
|
||||
|
||||
transition: (function checkTransition() {
|
||||
const style = testDiv.style;
|
||||
return ('transition' in style || 'webkitTransition' in style || 'MozTransition' in style);
|
||||
}()),
|
||||
transforms3d: (win.Modernizr && win.Modernizr.csstransforms3d === true) || (function checkTransforms3d() {
|
||||
transforms3d: (win$1.Modernizr && win$1.Modernizr.csstransforms3d === true) || (function checkTransforms3d() {
|
||||
const style = testDiv.style;
|
||||
return ('webkitPerspective' in style || 'MozPerspective' in style || 'OPerspective' in style || 'MsPerspective' in style || 'perspective' in style);
|
||||
}()),
|
||||
@@ -957,7 +1087,7 @@ const Support = (function Support() {
|
||||
}()),
|
||||
|
||||
observer: (function checkObserver() {
|
||||
return ('MutationObserver' in win || 'WebkitMutationObserver' in win);
|
||||
return ('MutationObserver' in win$1 || 'WebkitMutationObserver' in win$1);
|
||||
}()),
|
||||
|
||||
passiveListener: (function checkPassiveListener() {
|
||||
@@ -969,7 +1099,7 @@ const Support = (function Support() {
|
||||
supportsPassive = true;
|
||||
},
|
||||
});
|
||||
win.addEventListener('testPassiveListener', null, opts);
|
||||
win$1.addEventListener('testPassiveListener', null, opts);
|
||||
} catch (e) {
|
||||
// No support
|
||||
}
|
||||
@@ -977,21 +1107,21 @@ const Support = (function Support() {
|
||||
}()),
|
||||
|
||||
gestures: (function checkGestures() {
|
||||
return 'ongesturestart' in win;
|
||||
return 'ongesturestart' in win$1;
|
||||
}()),
|
||||
};
|
||||
}());
|
||||
|
||||
const Browser = (function Browser() {
|
||||
function isSafari() {
|
||||
const ua = win.navigator.userAgent.toLowerCase();
|
||||
const ua = win$1.navigator.userAgent.toLowerCase();
|
||||
return (ua.indexOf('safari') >= 0 && ua.indexOf('chrome') < 0 && ua.indexOf('android') < 0);
|
||||
}
|
||||
return {
|
||||
isIE: !!win.navigator.userAgent.match(/Trident/g) || !!win.navigator.userAgent.match(/MSIE/g),
|
||||
isEdge: !!win.navigator.userAgent.match(/Edge/g),
|
||||
isIE: !!win$1.navigator.userAgent.match(/Trident/g) || !!win$1.navigator.userAgent.match(/MSIE/g),
|
||||
isEdge: !!win$1.navigator.userAgent.match(/Edge/g),
|
||||
isSafari: isSafari(),
|
||||
isUiWebView: /(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/i.test(win.navigator.userAgent),
|
||||
isUiWebView: /(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/i.test(win$1.navigator.userAgent),
|
||||
};
|
||||
}());
|
||||
|
||||
@@ -1308,7 +1438,7 @@ function updateSlides () {
|
||||
if (slide.css('display') === 'none') continue; // eslint-disable-line
|
||||
|
||||
if (params.slidesPerView === 'auto') {
|
||||
const slideStyles = win.getComputedStyle(slide[0], null);
|
||||
const slideStyles = win$1.getComputedStyle(slide[0], null);
|
||||
const currentTransform = slide[0].style.transform;
|
||||
const currentWebKitTransform = slide[0].style.webkitTransform;
|
||||
if (currentTransform) {
|
||||
@@ -2169,7 +2299,7 @@ function loopCreate () {
|
||||
const blankSlidesNum = params.slidesPerGroup - (slides.length % params.slidesPerGroup);
|
||||
if (blankSlidesNum !== params.slidesPerGroup) {
|
||||
for (let i = 0; i < blankSlidesNum; i += 1) {
|
||||
const blankNode = $(doc.createElement('div')).addClass(`${params.slideClass} ${params.slideBlankClass}`);
|
||||
const blankNode = $(doc$1.createElement('div')).addClass(`${params.slideClass} ${params.slideBlankClass}`);
|
||||
$wrapperEl.append(blankNode);
|
||||
}
|
||||
slides = $wrapperEl.children(`.${params.slideClass}`);
|
||||
@@ -2427,7 +2557,7 @@ var manipulation = {
|
||||
};
|
||||
|
||||
const Device = (function Device() {
|
||||
const ua = win.navigator.userAgent;
|
||||
const ua = win$1.navigator.userAgent;
|
||||
|
||||
const device = {
|
||||
ios: false,
|
||||
@@ -2438,8 +2568,8 @@ const Device = (function Device() {
|
||||
iphone: false,
|
||||
ipod: false,
|
||||
ipad: false,
|
||||
cordova: win.cordova || win.phonegap,
|
||||
phonegap: win.cordova || win.phonegap,
|
||||
cordova: win$1.cordova || win$1.phonegap,
|
||||
phonegap: win$1.cordova || win$1.phonegap,
|
||||
};
|
||||
|
||||
const windows = ua.match(/(Windows Phone);?[\s\/]+([\d.]+)?/); // eslint-disable-line
|
||||
@@ -2495,7 +2625,7 @@ const Device = (function Device() {
|
||||
// Minimal UI
|
||||
if (device.os && device.os === 'ios') {
|
||||
const osVersionArr = device.osVersion.split('.');
|
||||
const metaViewport = doc.querySelector('meta[name="viewport"]');
|
||||
const metaViewport = doc$1.querySelector('meta[name="viewport"]');
|
||||
device.minimalUi = !device.webView
|
||||
&& (ipod || iphone)
|
||||
&& (osVersionArr[0] * 1 === 7 ? osVersionArr[1] * 1 >= 1 : osVersionArr[0] * 1 > 7)
|
||||
@@ -2503,7 +2633,7 @@ const Device = (function Device() {
|
||||
}
|
||||
|
||||
// Pixel Ratio
|
||||
device.pixelRatio = win.devicePixelRatio || 1;
|
||||
device.pixelRatio = win$1.devicePixelRatio || 1;
|
||||
|
||||
// Export object
|
||||
return device;
|
||||
@@ -2542,7 +2672,7 @@ function onTouchStart (event) {
|
||||
if (
|
||||
edgeSwipeDetection
|
||||
&& ((startX <= edgeSwipeThreshold)
|
||||
|| (startX >= win.screen.width - edgeSwipeThreshold))
|
||||
|| (startX >= win$1.screen.width - edgeSwipeThreshold))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@@ -2566,11 +2696,11 @@ function onTouchStart (event) {
|
||||
let preventDefault = true;
|
||||
if ($(e.target).is(data.formElements)) preventDefault = false;
|
||||
if (
|
||||
doc.activeElement
|
||||
&& $(doc.activeElement).is(data.formElements)
|
||||
&& doc.activeElement !== e.target
|
||||
doc$1.activeElement
|
||||
&& $(doc$1.activeElement).is(data.formElements)
|
||||
&& doc$1.activeElement !== e.target
|
||||
) {
|
||||
doc.activeElement.blur();
|
||||
doc$1.activeElement.blur();
|
||||
}
|
||||
|
||||
const shouldPreventDefault = preventDefault && swiper.allowTouchMove && params.touchStartPreventDefault;
|
||||
@@ -2633,8 +2763,8 @@ function onTouchMove (event) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (data.isTouchEvent && doc.activeElement) {
|
||||
if (e.target === doc.activeElement && $(e.target).is(data.formElements)) {
|
||||
if (data.isTouchEvent && doc$1.activeElement) {
|
||||
if (e.target === doc$1.activeElement && $(e.target).is(data.formElements)) {
|
||||
data.isMoved = true;
|
||||
swiper.allowClick = false;
|
||||
return;
|
||||
@@ -3133,8 +3263,8 @@ function attachEvents() {
|
||||
{
|
||||
if (!Support.touch && (Support.pointerEvents || Support.prefixedPointerEvents)) {
|
||||
target.addEventListener(touchEvents.start, swiper.onTouchStart, false);
|
||||
doc.addEventListener(touchEvents.move, swiper.onTouchMove, capture);
|
||||
doc.addEventListener(touchEvents.end, swiper.onTouchEnd, false);
|
||||
doc$1.addEventListener(touchEvents.move, swiper.onTouchMove, capture);
|
||||
doc$1.addEventListener(touchEvents.end, swiper.onTouchEnd, false);
|
||||
} else {
|
||||
if (Support.touch) {
|
||||
const passiveListener = touchEvents.start === 'touchstart' && Support.passiveListener && params.passiveListeners ? { passive: true, capture: false } : false;
|
||||
@@ -3144,8 +3274,8 @@ function attachEvents() {
|
||||
}
|
||||
if ((params.simulateTouch && !Device.ios && !Device.android) || (params.simulateTouch && !Support.touch && Device.ios)) {
|
||||
target.addEventListener('mousedown', swiper.onTouchStart, false);
|
||||
doc.addEventListener('mousemove', swiper.onTouchMove, capture);
|
||||
doc.addEventListener('mouseup', swiper.onTouchEnd, false);
|
||||
doc$1.addEventListener('mousemove', swiper.onTouchMove, capture);
|
||||
doc$1.addEventListener('mouseup', swiper.onTouchEnd, false);
|
||||
}
|
||||
}
|
||||
// Prevent Links Clicks
|
||||
@@ -3172,8 +3302,8 @@ function detachEvents() {
|
||||
{
|
||||
if (!Support.touch && (Support.pointerEvents || Support.prefixedPointerEvents)) {
|
||||
target.removeEventListener(touchEvents.start, swiper.onTouchStart, false);
|
||||
doc.removeEventListener(touchEvents.move, swiper.onTouchMove, capture);
|
||||
doc.removeEventListener(touchEvents.end, swiper.onTouchEnd, false);
|
||||
doc$1.removeEventListener(touchEvents.move, swiper.onTouchMove, capture);
|
||||
doc$1.removeEventListener(touchEvents.end, swiper.onTouchEnd, false);
|
||||
} else {
|
||||
if (Support.touch) {
|
||||
const passiveListener = touchEvents.start === 'onTouchStart' && Support.passiveListener && params.passiveListeners ? { passive: true, capture: false } : false;
|
||||
@@ -3183,8 +3313,8 @@ function detachEvents() {
|
||||
}
|
||||
if ((params.simulateTouch && !Device.ios && !Device.android) || (params.simulateTouch && !Support.touch && Device.ios)) {
|
||||
target.removeEventListener('mousedown', swiper.onTouchStart, false);
|
||||
doc.removeEventListener('mousemove', swiper.onTouchMove, capture);
|
||||
doc.removeEventListener('mouseup', swiper.onTouchEnd, false);
|
||||
doc$1.removeEventListener('mousemove', swiper.onTouchMove, capture);
|
||||
doc$1.removeEventListener('mouseup', swiper.onTouchEnd, false);
|
||||
}
|
||||
}
|
||||
// Prevent Links Clicks
|
||||
@@ -3271,10 +3401,10 @@ function getBreakpoint (breakpoints) {
|
||||
for (let i = 0; i < points.length; i += 1) {
|
||||
const point = points[i];
|
||||
if (swiper.params.breakpointsInverse) {
|
||||
if (point <= win.innerWidth) {
|
||||
if (point <= win$1.innerWidth) {
|
||||
breakpoint = point;
|
||||
}
|
||||
} else if (point >= win.innerWidth && !breakpoint) {
|
||||
} else if (point >= win$1.innerWidth && !breakpoint) {
|
||||
breakpoint = point;
|
||||
}
|
||||
}
|
||||
@@ -3342,7 +3472,7 @@ function loadImage (imageEl, src, srcset, sizes, checkForComplete, callback) {
|
||||
}
|
||||
if (!imageEl.complete || !checkForComplete) {
|
||||
if (src) {
|
||||
image = new win.Image();
|
||||
image = new win$1.Image();
|
||||
image.onload = onReady;
|
||||
image.onerror = onReady;
|
||||
if (sizes) {
|
||||
@@ -4064,21 +4194,21 @@ var Resize = {
|
||||
init() {
|
||||
const swiper = this;
|
||||
// Emit resize
|
||||
win.addEventListener('resize', swiper.resize.resizeHandler);
|
||||
win$1.addEventListener('resize', swiper.resize.resizeHandler);
|
||||
|
||||
// Emit orientationchange
|
||||
win.addEventListener('orientationchange', swiper.resize.orientationChangeHandler);
|
||||
win$1.addEventListener('orientationchange', swiper.resize.orientationChangeHandler);
|
||||
},
|
||||
destroy() {
|
||||
const swiper = this;
|
||||
win.removeEventListener('resize', swiper.resize.resizeHandler);
|
||||
win.removeEventListener('orientationchange', swiper.resize.orientationChangeHandler);
|
||||
win$1.removeEventListener('resize', swiper.resize.resizeHandler);
|
||||
win$1.removeEventListener('orientationchange', swiper.resize.orientationChangeHandler);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Observer = {
|
||||
func: win.MutationObserver || win.WebkitMutationObserver,
|
||||
func: win$1.MutationObserver || win$1.WebkitMutationObserver,
|
||||
attach(target, options = {}) {
|
||||
const swiper = this;
|
||||
|
||||
@@ -4095,10 +4225,10 @@ const Observer = {
|
||||
swiper.emit('observerUpdate', mutations[0]);
|
||||
};
|
||||
|
||||
if (win.requestAnimationFrame) {
|
||||
win.requestAnimationFrame(observerUpdate);
|
||||
if (win$1.requestAnimationFrame) {
|
||||
win$1.requestAnimationFrame(observerUpdate);
|
||||
} else {
|
||||
win.setTimeout(observerUpdate, 0);
|
||||
win$1.setTimeout(observerUpdate, 0);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -4166,23 +4296,23 @@ var Observer$1 = {
|
||||
|
||||
function isEventSupported() {
|
||||
const eventName = 'onwheel';
|
||||
let isSupported = eventName in doc;
|
||||
let isSupported = eventName in doc$1;
|
||||
|
||||
if (!isSupported) {
|
||||
const element = doc.createElement('div');
|
||||
const element = doc$1.createElement('div');
|
||||
element.setAttribute(eventName, 'return;');
|
||||
isSupported = typeof element[eventName] === 'function';
|
||||
}
|
||||
|
||||
if (!isSupported
|
||||
&& doc.implementation
|
||||
&& doc.implementation.hasFeature
|
||||
&& doc$1.implementation
|
||||
&& doc$1.implementation.hasFeature
|
||||
// always returns true in newer browsers as per the standard.
|
||||
// @see http://dom.spec.whatwg.org/#dom-domimplementation-hasfeature
|
||||
&& doc.implementation.hasFeature('', '') !== true
|
||||
&& doc$1.implementation.hasFeature('', '') !== true
|
||||
) {
|
||||
// This is the only way to test support for the `wheel` event in IE9+.
|
||||
isSupported = doc.implementation.hasFeature('Events.wheel', '3.0');
|
||||
isSupported = doc$1.implementation.hasFeature('Events.wheel', '3.0');
|
||||
}
|
||||
|
||||
return isSupported;
|
||||
@@ -4190,7 +4320,7 @@ function isEventSupported() {
|
||||
const Mousewheel = {
|
||||
lastScrollTime: Utils.now(),
|
||||
event: (function getEvent() {
|
||||
if (win.navigator.userAgent.indexOf('firefox') > -1) return 'DOMMouseScroll';
|
||||
if (win$1.navigator.userAgent.indexOf('firefox') > -1) return 'DOMMouseScroll';
|
||||
return isEventSupported() ? 'wheel' : 'mousewheel';
|
||||
}()),
|
||||
normalize(e) {
|
||||
@@ -4306,7 +4436,7 @@ const Mousewheel = {
|
||||
swiper.emit('scroll', e);
|
||||
} else if (params.releaseOnEdges) return true;
|
||||
}
|
||||
swiper.mousewheel.lastScrollTime = (new win.Date()).getTime();
|
||||
swiper.mousewheel.lastScrollTime = (new win$1.Date()).getTime();
|
||||
} else {
|
||||
// Freemode or scrollContainer:
|
||||
if (swiper.params.loop) {
|
||||
@@ -4912,8 +5042,8 @@ const Scrollbar = {
|
||||
const passiveListener = Support.passiveListener && params.passiveListeners ? { passive: true, capture: false } : false;
|
||||
if (!Support.touch) {
|
||||
target.addEventListener(touchEventsDesktop.start, swiper.scrollbar.onDragStart, activeListener);
|
||||
doc.addEventListener(touchEventsDesktop.move, swiper.scrollbar.onDragMove, activeListener);
|
||||
doc.addEventListener(touchEventsDesktop.end, swiper.scrollbar.onDragEnd, passiveListener);
|
||||
doc$1.addEventListener(touchEventsDesktop.move, swiper.scrollbar.onDragMove, activeListener);
|
||||
doc$1.addEventListener(touchEventsDesktop.end, swiper.scrollbar.onDragEnd, passiveListener);
|
||||
} else {
|
||||
target.addEventListener(touchEventsTouch.start, swiper.scrollbar.onDragStart, activeListener);
|
||||
target.addEventListener(touchEventsTouch.move, swiper.scrollbar.onDragMove, activeListener);
|
||||
@@ -4932,8 +5062,8 @@ const Scrollbar = {
|
||||
const passiveListener = Support.passiveListener && params.passiveListeners ? { passive: true, capture: false } : false;
|
||||
if (!Support.touch) {
|
||||
target.removeEventListener(touchEventsDesktop.start, swiper.scrollbar.onDragStart, activeListener);
|
||||
doc.removeEventListener(touchEventsDesktop.move, swiper.scrollbar.onDragMove, activeListener);
|
||||
doc.removeEventListener(touchEventsDesktop.end, swiper.scrollbar.onDragEnd, passiveListener);
|
||||
doc$1.removeEventListener(touchEventsDesktop.move, swiper.scrollbar.onDragMove, activeListener);
|
||||
doc$1.removeEventListener(touchEventsDesktop.end, swiper.scrollbar.onDragEnd, passiveListener);
|
||||
} else {
|
||||
target.removeEventListener(touchEventsTouch.start, swiper.scrollbar.onDragStart, activeListener);
|
||||
target.removeEventListener(touchEventsTouch.move, swiper.scrollbar.onDragMove, activeListener);
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
|
||||
box-shadow: $toggle-ios-handle-box-shadow;
|
||||
will-change: transform;
|
||||
|
||||
contain: strict;
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
|
||||
box-shadow: $toggle-md-handle-box-shadow;
|
||||
will-change: transform, background-color;
|
||||
|
||||
contain: strict;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ ion-virtual-scroll > .virtual-loading {
|
||||
}
|
||||
|
||||
ion-virtual-scroll > .virtual-item {
|
||||
/* stylelint-disable declaration-no-important, property-blacklist */
|
||||
/* stylelint-disable declaration-no-important, property-disallowed-list */
|
||||
position: absolute !important;
|
||||
|
||||
top: 0 !important;
|
||||
|
||||
@@ -128,8 +128,10 @@ export const createAnimation = (): Animation => {
|
||||
|
||||
webAnimations.length = 0;
|
||||
} else {
|
||||
elements.forEach(element => {
|
||||
raf(() => {
|
||||
const elementsArray = elements.slice();
|
||||
|
||||
raf(() => {
|
||||
elementsArray.forEach(element => {
|
||||
removeStyleProperty(element, 'animation-name');
|
||||
removeStyleProperty(element, 'animation-duration');
|
||||
removeStyleProperty(element, 'animation-timing-function');
|
||||
|
||||
@@ -17,6 +17,9 @@ export class Point {
|
||||
* P1: (0.32, 0.72)
|
||||
* P2: (0, 1)
|
||||
* P3: (1, 1)
|
||||
*
|
||||
* If you give a cubic bezier curve that never reaches the
|
||||
* provided progression, this function will return NaN.
|
||||
*/
|
||||
export const getTimeGivenProgression = (p0: Point, p1: Point, p2: Point, p3: Point, progression: number) => {
|
||||
const tValues = solveCubicBezier(p0.y, p1.y, p2.y, p3.y, progression);
|
||||
|
||||
@@ -243,6 +243,19 @@ const setZIndex = (
|
||||
}
|
||||
};
|
||||
|
||||
export const getIonPageElement = (element: HTMLElement) => {
|
||||
if (element.classList.contains('ion-page')) {
|
||||
return element;
|
||||
}
|
||||
|
||||
const ionPage = element.querySelector(':scope > .ion-page, :scope > ion-nav, :scope > ion-tabs');
|
||||
if (ionPage) {
|
||||
return ionPage;
|
||||
}
|
||||
// idk, return the original element so at least something animates and we don't have a null pointer
|
||||
return element;
|
||||
};
|
||||
|
||||
export interface TransitionOptions extends NavOptions {
|
||||
progressCallback?: ((ani: IonicAnimation | Animation | undefined) => void);
|
||||
baseEl: any;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { IonicAnimation } from '../../interface';
|
||||
import { createAnimation } from '../animation/animation';
|
||||
import { TransitionOptions } from '../transition';
|
||||
import { TransitionOptions, getIonPageElement } from '../transition';
|
||||
|
||||
const DURATION = 540;
|
||||
const addSafeArea = (val: number, side = 'top'): string => {
|
||||
@@ -376,6 +376,13 @@ export const iosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOptio
|
||||
.beforeClearStyles([OPACITY])
|
||||
.fromTo('transform', `translateX(${CENTER})`, (isRTL ? 'translateX(-100%)' : 'translateX(100%)'));
|
||||
|
||||
const leavingPage = getIonPageElement(leavingEl) as HTMLElement;
|
||||
rootAnimation.afterAddWrite(() => {
|
||||
if (rootAnimation.getDirection() === 'normal') {
|
||||
leavingPage.style.setProperty('display', 'none');
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
// leaving content, forward direction
|
||||
leavingContent
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { IonicAnimation } from '../../interface';
|
||||
import { createAnimation } from '../animation/animation';
|
||||
import { TransitionOptions } from '../transition';
|
||||
import { TransitionOptions, getIonPageElement } from '../transition';
|
||||
|
||||
export const mdTransitionAnimation = (_: HTMLElement, opts: TransitionOptions): IonicAnimation => {
|
||||
const OFF_BOTTOM = '40px';
|
||||
@@ -59,16 +59,3 @@ export const mdTransitionAnimation = (_: HTMLElement, opts: TransitionOptions):
|
||||
|
||||
return rootTransition;
|
||||
};
|
||||
|
||||
const getIonPageElement = (element: HTMLElement) => {
|
||||
if (element.classList.contains('ion-page')) {
|
||||
return element;
|
||||
}
|
||||
|
||||
const ionPage = element.querySelector(':scope > .ion-page, :scope > ion-nav, :scope > ion-tabs');
|
||||
if (ionPage) {
|
||||
return ionPage;
|
||||
}
|
||||
// idk, return the original element so at least something animates and we don't have a null pointer
|
||||
return element;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/docs",
|
||||
"version": "4.11.0",
|
||||
"version": "4.11.13",
|
||||
"description": "Pre-packaged API documentation for the Ionic docs.",
|
||||
"main": "core.json",
|
||||
"types": "core.d.ts",
|
||||
|
||||
1
packages/react-router/empty-module.js
vendored
Normal file
1
packages/react-router/empty-module.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = ''
|
||||
@@ -1,7 +0,0 @@
|
||||
window.matchMedia = window.matchMedia || function() {
|
||||
return {
|
||||
matches : false,
|
||||
addListener : function() {},
|
||||
removeListener: function() {}
|
||||
};
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/react-router",
|
||||
"version": "4.11.0",
|
||||
"version": "4.11.13",
|
||||
"description": "React Router wrapper for @ionic/react",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -36,54 +36,64 @@
|
||||
"dist/"
|
||||
],
|
||||
"dependencies": {
|
||||
"tslib": "*",
|
||||
"tslint": "^5.20.0",
|
||||
"tslint-ionic-rules": "0.0.21",
|
||||
"tslint-react": "^4.1.0"
|
||||
"tslib": "*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ionic/core": "^4.10.0",
|
||||
"@ionic/react": "4.10.0-rc.3",
|
||||
"@ionic/core": "4.11.13",
|
||||
"@ionic/react": "4.11.13",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-router": "^5.0.1",
|
||||
"react-router-dom": "^5.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ionic/core": "4.11.0",
|
||||
"@ionic/react": "4.11.0",
|
||||
"@types/jest": "^23.3.9",
|
||||
"@types/node": "12.6.9",
|
||||
"@ionic/core": "4.11.13",
|
||||
"@ionic/react": "4.11.13",
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.3.2",
|
||||
"@testing-library/user-event": "^7.1.2",
|
||||
"@types/jest": "^24.0.23",
|
||||
"@types/node": "^12.12.14",
|
||||
"@types/react": "^16.9.2",
|
||||
"@types/react-dom": "^16.9.0",
|
||||
"@types/react-router": "^5.0.3",
|
||||
"@types/react-router-dom": "^4.3.1",
|
||||
"jest": "^24.8.0",
|
||||
"jest-dom": "^3.4.0",
|
||||
"jest": "^24.9.0",
|
||||
"jest-dom": "^4.0.0",
|
||||
"np": "^5.0.1",
|
||||
"react": "^16.9.0",
|
||||
"react-dom": "^16.9.0",
|
||||
"react-router": "^5.0.1",
|
||||
"react-router-dom": "^5.0.1",
|
||||
"react-testing-library": "^7.0.0",
|
||||
"rollup": "^1.18.0",
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
"rollup-plugin-sourcemaps": "^0.4.2",
|
||||
"ts-jest": "^24.0.2",
|
||||
"typescript": "3.5.3"
|
||||
"tslint": "^5.20.0",
|
||||
"tslint-ionic-rules": "0.0.21",
|
||||
"tslint-react": "^4.1.0",
|
||||
"typescript": "^3.7.2"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "ts-jest",
|
||||
"setupFilesAfterEnv": [
|
||||
"<rootDir>/jest.setup.js"
|
||||
"<rootDir>/setupTests.ts"
|
||||
],
|
||||
"testPathIgnorePatterns": [
|
||||
"node_modules",
|
||||
"dist-transpiled",
|
||||
"dist"
|
||||
],
|
||||
"globals": {
|
||||
"ts-jest": {
|
||||
"diagnostics": false
|
||||
}
|
||||
},
|
||||
"modulePaths": [
|
||||
"<rootDir>"
|
||||
]
|
||||
],
|
||||
"moduleNameMapper": {
|
||||
"\\.(css|jpg|png|svg)$": "<rootDir>/empty-module.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
9
packages/react-router/setupTests.ts
Normal file
9
packages/react-router/setupTests.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
|
||||
window.matchMedia = window.matchMedia || function() {
|
||||
return {
|
||||
matches: false,
|
||||
addListener() { },
|
||||
removeListener() { }
|
||||
};
|
||||
} as any;
|
||||
15
packages/react-router/src/ReactRouter/IonReactHashRouter.tsx
Normal file
15
packages/react-router/src/ReactRouter/IonReactHashRouter.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import { HashRouter, HashRouterProps } from 'react-router-dom';
|
||||
|
||||
import { RouteManagerWithRouter } from './Router';
|
||||
|
||||
export class IonReactHashRouter extends React.Component<HashRouterProps> {
|
||||
render() {
|
||||
const { children, ...props } = this.props;
|
||||
return (
|
||||
<HashRouter {...props}>
|
||||
<RouteManagerWithRouter>{children}</RouteManagerWithRouter>
|
||||
</HashRouter>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { MemoryHistory } from 'history';
|
||||
import React from 'react';
|
||||
import { MemoryRouter, MemoryRouterProps, matchPath } from 'react-router';
|
||||
|
||||
import { LocationState, RouteManager } from './Router';
|
||||
|
||||
interface IonReactMemoryRouterProps extends MemoryRouterProps {
|
||||
history: MemoryHistory<LocationState>;
|
||||
}
|
||||
|
||||
export class IonReactMemoryRouter extends React.Component<IonReactMemoryRouterProps> {
|
||||
render() {
|
||||
const { children, history, ...props } = this.props;
|
||||
const match = matchPath(history.location.pathname, this.props);
|
||||
return (
|
||||
<MemoryRouter {...props}>
|
||||
<RouteManager history={history} location={history.location} match={match!}>{children}</RouteManager>
|
||||
</MemoryRouter>
|
||||
);
|
||||
}
|
||||
}
|
||||
15
packages/react-router/src/ReactRouter/IonReactRouter.tsx
Normal file
15
packages/react-router/src/ReactRouter/IonReactRouter.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import { BrowserRouter, BrowserRouterProps } from 'react-router-dom';
|
||||
|
||||
import { RouteManagerWithRouter } from './Router';
|
||||
|
||||
export class IonReactRouter extends React.Component<BrowserRouterProps> {
|
||||
render() {
|
||||
const { children, ...props } = this.props;
|
||||
return (
|
||||
<BrowserRouter {...props}>
|
||||
<RouteManagerWithRouter>{children}</RouteManagerWithRouter>
|
||||
</BrowserRouter>
|
||||
);
|
||||
}
|
||||
}
|
||||
1
packages/react-router/src/ReactRouter/IonRouteAction.ts
Normal file
1
packages/react-router/src/ReactRouter/IonRouteAction.ts
Normal file
@@ -0,0 +1 @@
|
||||
export type IonRouteAction = 'push' | 'replace' | 'pop';
|
||||
@@ -1,6 +1,6 @@
|
||||
import { RouteProps, match } from 'react-router-dom';
|
||||
|
||||
export interface IonRouteData {
|
||||
match: match<{ tab: string }> | null;
|
||||
match: match | null;
|
||||
childProps: RouteProps;
|
||||
}
|
||||
|
||||
@@ -4,31 +4,23 @@ import { Location as HistoryLocation, UnregisterCallback } from 'history';
|
||||
import React from 'react';
|
||||
import { RouteComponentProps } from 'react-router-dom';
|
||||
|
||||
import { generateId } from '../utils';
|
||||
import { LocationHistory } from '../utils/LocationHistory';
|
||||
|
||||
import { IonRouteAction } from './IonRouteAction';
|
||||
import { StackManager } from './StackManager';
|
||||
import { ViewItem } from './ViewItem';
|
||||
import { ViewStack } from './ViewStacks';
|
||||
|
||||
interface NavManagerProps extends RouteComponentProps {
|
||||
findViewInfoByLocation: (location: HistoryLocation) => { view?: ViewItem, viewStack?: ViewStack };
|
||||
findViewInfoById: (id: string) => { view?: ViewItem, viewStack?: ViewStack };
|
||||
getActiveIonPage: () => { view?: ViewItem, viewStack?: ViewStack };
|
||||
onNavigateBack: (defaultHref?: string) => void;
|
||||
onNavigate: (ionRouteAction: IonRouteAction, path: string, state?: any) => void;
|
||||
}
|
||||
|
||||
export class NavManager extends React.Component<NavManagerProps, NavContextState> {
|
||||
|
||||
listenUnregisterCallback: UnregisterCallback | undefined;
|
||||
locationHistory: LocationHistory = new LocationHistory();
|
||||
|
||||
constructor(props: NavManagerProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
goBack: this.goBack.bind(this),
|
||||
hasIonicRouter: () => true,
|
||||
getHistory: this.getHistory.bind(this),
|
||||
getLocation: this.getLocation.bind(this),
|
||||
navigate: this.navigate.bind(this),
|
||||
getStackManager: this.getStackManager.bind(this),
|
||||
getPageManager: this.getPageManager.bind(this),
|
||||
@@ -40,16 +32,15 @@ export class NavManager extends React.Component<NavManagerProps, NavContextState
|
||||
this.setState({
|
||||
currentPath: location.pathname
|
||||
});
|
||||
this.locationHistory.add(location);
|
||||
});
|
||||
|
||||
this.locationHistory.add({
|
||||
hash: window.location.hash,
|
||||
key: generateId(),
|
||||
pathname: window.location.pathname,
|
||||
search: window.location.search,
|
||||
state: {}
|
||||
});
|
||||
if (document) {
|
||||
document.addEventListener('ionBackButton', (e: any) => {
|
||||
e.detail.register(0, () => {
|
||||
this.props.history.goBack();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@@ -59,38 +50,11 @@ export class NavManager extends React.Component<NavManagerProps, NavContextState
|
||||
}
|
||||
|
||||
goBack(defaultHref?: string) {
|
||||
const { view: activeIonPage } = this.props.getActiveIonPage();
|
||||
if (activeIonPage) {
|
||||
const { view: enteringView } = this.props.findViewInfoById(activeIonPage.prevId!);
|
||||
if (enteringView) {
|
||||
const lastLocation = this.locationHistory.findLastLocation(enteringView.routeData.match.url);
|
||||
if (lastLocation) {
|
||||
this.props.history.replace(lastLocation.pathname + lastLocation.search, { direction: 'back' });
|
||||
} else {
|
||||
this.props.history.replace(enteringView.routeData.match.url, { direction: 'back' });
|
||||
}
|
||||
} else {
|
||||
if (defaultHref) {
|
||||
this.props.history.replace(defaultHref, { direction: 'back' });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (defaultHref) {
|
||||
this.props.history.replace(defaultHref, { direction: 'back' });
|
||||
}
|
||||
}
|
||||
this.props.onNavigateBack(defaultHref);
|
||||
}
|
||||
|
||||
getHistory() {
|
||||
return this.props.history as any;
|
||||
}
|
||||
|
||||
getLocation() {
|
||||
return this.props.location as any;
|
||||
}
|
||||
|
||||
navigate(path: string, direction?: RouterDirection | 'none') {
|
||||
this.props.history.push(path, { direction });
|
||||
navigate(path: string, direction?: RouterDirection | 'none', ionRouteAction: IonRouteAction = 'push') {
|
||||
this.props.onNavigate(ionRouteAction, path, direction);
|
||||
}
|
||||
|
||||
getPageManager() {
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
import { NavDirection } from '@ionic/core';
|
||||
import React, { ReactNode } from 'react';
|
||||
|
||||
import { ViewStacks } from './ViewStacks';
|
||||
|
||||
export interface RouteManagerContextState {
|
||||
syncView: (page: HTMLElement, viewId: string) => void;
|
||||
syncRoute: (id: string, route: any) => void;
|
||||
hideView: (viewId: string) => void;
|
||||
viewStacks: ViewStacks;
|
||||
setupIonRouter: (id: string, children: ReactNode, routerOutlet: HTMLIonRouterOutletElement) => Promise<void>;
|
||||
setupIonRouter: (id: string, children: ReactNode, routerOutlet: HTMLIonRouterOutletElement) => void;
|
||||
removeViewStack: (stack: string) => void;
|
||||
transitionView: (enteringEl: HTMLElement, leavingEl: HTMLElement, ionRouterOuter: HTMLIonRouterOutletElement, direction: NavDirection) => void;
|
||||
getRoute: (id: string) => any;
|
||||
}
|
||||
|
||||
export const RouteManagerContext = /*@__PURE__*/React.createContext<RouteManagerContextState>({
|
||||
viewStacks: new ViewStacks(),
|
||||
syncView: () => { navContextNotFoundError(); },
|
||||
syncRoute: () => { navContextNotFoundError(); },
|
||||
hideView: () => { navContextNotFoundError(); },
|
||||
setupIonRouter: () => Promise.reject(navContextNotFoundError()),
|
||||
removeViewStack: () => { navContextNotFoundError(); },
|
||||
transitionView: () => { navContextNotFoundError(); }
|
||||
getRoute: () => { navContextNotFoundError(); }
|
||||
});
|
||||
|
||||
function navContextNotFoundError() {
|
||||
|
||||
@@ -1,109 +1,176 @@
|
||||
import { NavDirection } from '@ionic/core';
|
||||
import { RouterDirection } from '@ionic/react';
|
||||
import { RouterDirection, getConfig } from '@ionic/react';
|
||||
import { Action as HistoryAction, Location as HistoryLocation, UnregisterCallback } from 'history';
|
||||
import React from 'react';
|
||||
import { BrowserRouter, BrowserRouterProps, RouteComponentProps, matchPath, withRouter } from 'react-router-dom';
|
||||
import { RouteComponentProps, matchPath, withRouter } from 'react-router-dom';
|
||||
|
||||
import { generateId } from '../utils';
|
||||
import { generateId, isDevMode } from '../utils';
|
||||
import { LocationHistory } from '../utils/LocationHistory';
|
||||
|
||||
import { IonRouteAction } from './IonRouteAction';
|
||||
import { IonRouteData } from './IonRouteData';
|
||||
import { NavManager } from './NavManager';
|
||||
import { RouteManagerContext, RouteManagerContextState } from './RouteManagerContext';
|
||||
import { ViewItem } from './ViewItem';
|
||||
import { ViewStack, ViewStacks } from './ViewStacks';
|
||||
|
||||
interface RouteManagerState extends RouteManagerContextState {
|
||||
location?: HistoryLocation;
|
||||
action?: HistoryAction;
|
||||
export interface LocationState {
|
||||
direction?: RouterDirection;
|
||||
action?: IonRouteAction;
|
||||
}
|
||||
|
||||
class RouteManager extends React.Component<RouteComponentProps, RouteManagerState> {
|
||||
interface RouteManagerProps extends RouteComponentProps<{}, {}, LocationState> {
|
||||
location: HistoryLocation<LocationState>;
|
||||
}
|
||||
|
||||
interface RouteManagerState extends RouteManagerContextState {
|
||||
location?: HistoryLocation<LocationState>;
|
||||
action?: IonRouteAction;
|
||||
}
|
||||
|
||||
export class RouteManager extends React.Component<RouteManagerProps, RouteManagerState> {
|
||||
listenUnregisterCallback: UnregisterCallback | undefined;
|
||||
activeIonPageId?: string;
|
||||
currentIonRouteAction?: IonRouteAction;
|
||||
currentRouteDirection?: RouterDirection;
|
||||
locationHistory = new LocationHistory();
|
||||
routes: { [key: string]: React.ReactElement<any>; } = {};
|
||||
ionPageElements: { [key: string]: HTMLElement; } = {};
|
||||
routerOutlets: { [key: string]: HTMLIonRouterOutletElement; } = {};
|
||||
firstRender = true;
|
||||
|
||||
constructor(props: RouteComponentProps) {
|
||||
constructor(props: RouteManagerProps) {
|
||||
super(props);
|
||||
this.listenUnregisterCallback = this.props.history.listen(this.historyChange.bind(this));
|
||||
this.handleNavigate = this.handleNavigate.bind(this);
|
||||
this.navigateBack = this.navigateBack.bind(this);
|
||||
this.state = {
|
||||
viewStacks: new ViewStacks(),
|
||||
hideView: this.hideView.bind(this),
|
||||
setupIonRouter: this.setupIonRouter.bind(this),
|
||||
removeViewStack: this.removeViewStack.bind(this),
|
||||
syncView: this.syncView.bind(this),
|
||||
transitionView: this.transitionView.bind(this),
|
||||
syncRoute: this.syncRoute.bind(this),
|
||||
getRoute: this.getRoute.bind(this)
|
||||
};
|
||||
|
||||
this.locationHistory.add({
|
||||
hash: window.location.hash,
|
||||
key: generateId(),
|
||||
pathname: window.location.pathname,
|
||||
search: window.location.search,
|
||||
state: {}
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate(_prevProps: RouteComponentProps, prevState: RouteManagerState) {
|
||||
// Trigger a page change if the location or action is different
|
||||
if (this.state.location && prevState.location !== this.state.location || prevState.action !== this.state.action) {
|
||||
this.setActiveView(this.state.location!, this.state.action!);
|
||||
const viewStacks = Object.assign(new ViewStacks(), this.state.viewStacks);
|
||||
this.setActiveView(this.state.location!, this.state.action!, viewStacks);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.listenUnregisterCallback) {
|
||||
this.listenUnregisterCallback();
|
||||
}
|
||||
}
|
||||
|
||||
getRoute(id: string) {
|
||||
return this.routes[id];
|
||||
}
|
||||
|
||||
hideView(viewId: string) {
|
||||
const viewStacks = Object.assign(new ViewStacks(), this.state.viewStacks);
|
||||
const { view } = viewStacks.findViewInfoById(viewId);
|
||||
if (view) {
|
||||
view.show = false;
|
||||
view.ionPageElement = undefined;
|
||||
view.isIonRoute = false;
|
||||
view.prevId = undefined;
|
||||
view.key = generateId();
|
||||
delete this.ionPageElements[view.id];
|
||||
this.setState({
|
||||
viewStacks
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
historyChange(location: HistoryLocation, action: HistoryAction) {
|
||||
historyChange(location: HistoryLocation<LocationState>, action: HistoryAction) {
|
||||
const ionRouteAction = this.currentIonRouteAction === 'pop' ? 'pop' : action.toLowerCase() as IonRouteAction;
|
||||
let direction = this.currentRouteDirection;
|
||||
|
||||
if (ionRouteAction === 'push') {
|
||||
this.locationHistory.add(location);
|
||||
} else if (ionRouteAction === 'pop') {
|
||||
this.locationHistory.pop();
|
||||
direction = direction || 'back';
|
||||
} else if (ionRouteAction === 'replace') {
|
||||
this.locationHistory.replace(location);
|
||||
direction = 'none';
|
||||
}
|
||||
|
||||
if (direction === 'root') {
|
||||
this.locationHistory.clear();
|
||||
this.locationHistory.add(location);
|
||||
}
|
||||
|
||||
location.state = location.state || { direction };
|
||||
this.setState({
|
||||
location,
|
||||
action
|
||||
action: ionRouteAction as IonRouteAction
|
||||
});
|
||||
this.currentRouteDirection = undefined;
|
||||
this.currentIonRouteAction = undefined;
|
||||
}
|
||||
|
||||
setActiveView(location: HistoryLocation, action: HistoryAction) {
|
||||
const viewStacks = Object.assign(new ViewStacks(), this.state.viewStacks);
|
||||
let direction: RouterDirection = location.state && location.state.direction || 'forward';
|
||||
setActiveView(location: HistoryLocation<LocationState>, action: IonRouteAction, viewStacks: ViewStacks) {
|
||||
let direction: RouterDirection | undefined = (location.state && location.state.direction) || 'forward';
|
||||
let leavingView: ViewItem | undefined;
|
||||
const viewStackKeys = viewStacks.getKeys();
|
||||
let shouldTransitionPage = false;
|
||||
let leavingViewHtml: string | undefined;
|
||||
|
||||
viewStackKeys.forEach(key => {
|
||||
const { view: enteringView, viewStack: enteringViewStack, match } = viewStacks.findViewInfoByLocation(location, key);
|
||||
if (!enteringView || !enteringViewStack) {
|
||||
return;
|
||||
}
|
||||
|
||||
leavingView = viewStacks.findViewInfoById(this.activeIonPageId).view;
|
||||
|
||||
if (enteringView.isIonRoute) {
|
||||
enteringView.show = true;
|
||||
enteringView.mount = true;
|
||||
enteringView.routeData.match = match!;
|
||||
shouldTransitionPage = true;
|
||||
|
||||
this.activeIonPageId = enteringView.id;
|
||||
|
||||
if (leavingView) {
|
||||
if (direction === 'forward') {
|
||||
if (action === 'PUSH') {
|
||||
/**
|
||||
* If the page is being pushed into the stack by another view,
|
||||
* record the view that originally directed to the new view for back button purposes.
|
||||
*/
|
||||
enteringView.prevId = enteringView.prevId || leavingView.id;
|
||||
} else {
|
||||
direction = direction || 'back';
|
||||
leavingView.mount = false;
|
||||
}
|
||||
} else if (action === 'REPLACE') {
|
||||
if (action === 'push' && direction === 'forward') {
|
||||
/**
|
||||
* If the page is being pushed into the stack by another view,
|
||||
* record the view that originally directed to the new view for back button purposes.
|
||||
*/
|
||||
enteringView.prevId = leavingView.id;
|
||||
} else {
|
||||
leavingView.mount = false;
|
||||
this.removeOrphanedViews(enteringView, enteringViewStack);
|
||||
}
|
||||
|
||||
leavingViewHtml = enteringView.id === leavingView.id ? this.ionPageElements[leavingView.id].outerHTML : undefined;
|
||||
} else {
|
||||
// If there is not a leavingView, then we shouldn't provide a direction
|
||||
direction = undefined;
|
||||
}
|
||||
|
||||
} else {
|
||||
enteringView.show = true;
|
||||
enteringView.mount = true;
|
||||
enteringView.routeData.match = match!;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if (leavingView) {
|
||||
@@ -116,47 +183,102 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
|
||||
this.setState({
|
||||
viewStacks
|
||||
}, () => {
|
||||
const { view: enteringView, viewStack } = this.state.viewStacks.findViewInfoById(this.activeIonPageId);
|
||||
if (enteringView && viewStack) {
|
||||
const enteringEl = enteringView.ionPageElement ? enteringView.ionPageElement : undefined;
|
||||
const leavingEl = leavingView && leavingView.ionPageElement ? leavingView.ionPageElement : undefined;
|
||||
if (shouldTransitionPage) {
|
||||
const { view: enteringView, viewStack } = this.state.viewStacks.findViewInfoById(this.activeIonPageId);
|
||||
if (enteringView && viewStack) {
|
||||
const enteringEl = this.ionPageElements[enteringView.id];
|
||||
const leavingEl = leavingView && this.ionPageElements[leavingView.id];
|
||||
if (enteringEl) {
|
||||
let navDirection: NavDirection | undefined;
|
||||
if (leavingEl && leavingEl.innerHTML === '') {
|
||||
// Don't animate from an empty view
|
||||
navDirection = undefined;
|
||||
} else if (direction === 'none' || direction === 'root') {
|
||||
navDirection = undefined;
|
||||
} else {
|
||||
navDirection = direction;
|
||||
}
|
||||
const shouldGoBack = !!enteringView.prevId;
|
||||
const routerOutlet = this.routerOutlets[viewStack.id];
|
||||
this.commitView(
|
||||
enteringEl!,
|
||||
leavingEl!,
|
||||
routerOutlet,
|
||||
navDirection,
|
||||
shouldGoBack,
|
||||
leavingViewHtml);
|
||||
} else if (leavingEl) {
|
||||
leavingEl.classList.add('ion-page-hidden');
|
||||
leavingEl.setAttribute('aria-hidden', 'true');
|
||||
}
|
||||
}
|
||||
|
||||
if (enteringEl) {
|
||||
// Don't animate from an empty view
|
||||
const navDirection = leavingEl && leavingEl.innerHTML === '' ? undefined : direction === 'none' ? undefined : direction;
|
||||
this.transitionView(
|
||||
enteringEl!,
|
||||
leavingEl!,
|
||||
viewStack.routerOutlet,
|
||||
navDirection);
|
||||
} else if (leavingEl) {
|
||||
leavingEl.classList.add('ion-page-hidden');
|
||||
leavingEl.setAttribute('aria-hidden', 'true');
|
||||
// Warn if an IonPage was not eventually rendered in Dev Mode
|
||||
if (isDevMode()) {
|
||||
if (enteringView && enteringView.routeData.match!.url !== location.pathname) {
|
||||
setTimeout(() => {
|
||||
const { view } = this.state.viewStacks.findViewInfoById(this.activeIonPageId);
|
||||
if (view!.routeData.match!.url !== location.pathname) {
|
||||
console.warn('No IonPage was found to render. Make sure you wrap your page with an IonPage component.');
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.listenUnregisterCallback) {
|
||||
this.listenUnregisterCallback();
|
||||
}
|
||||
removeOrphanedViews(view: ViewItem, viewStack: ViewStack) {
|
||||
// Note: This technique is a bit wonky for views that reference each other and get into a circular loop.
|
||||
// It can still remove a view that probably shouldn't be.
|
||||
const viewsToRemove = viewStack.views.filter(v => v.prevId === view.id);
|
||||
viewsToRemove.forEach(v => {
|
||||
// Don't remove if view is currently active
|
||||
if (v.id !== this.activeIonPageId) {
|
||||
this.removeOrphanedViews(v, viewStack);
|
||||
|
||||
// If view is not currently visible, go ahead and remove it from DOM
|
||||
const page = this.ionPageElements[v.id];
|
||||
if (page.classList.contains('ion-page-hidden')) {
|
||||
v.show = false;
|
||||
v.isIonRoute = false;
|
||||
v.prevId = undefined;
|
||||
v.key = generateId();
|
||||
delete this.ionPageElements[v.id];
|
||||
}
|
||||
v.mount = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async setupIonRouter(id: string, children: any, routerOutlet: HTMLIonRouterOutletElement) {
|
||||
setupIonRouter(id: string, children: any, routerOutlet: HTMLIonRouterOutletElement) {
|
||||
const views: ViewItem[] = [];
|
||||
let activeId: string | undefined;
|
||||
const ionRouterOutlet = React.Children.only(children) as React.ReactElement;
|
||||
let foundMatch = false;
|
||||
React.Children.forEach(ionRouterOutlet.props.children, (child: React.ReactElement) => {
|
||||
views.push(createViewItem(child, this.props.history.location));
|
||||
const routeId = generateId();
|
||||
this.routes[routeId] = child;
|
||||
views.push(createViewItem(child, routeId, this.props.history.location));
|
||||
});
|
||||
|
||||
await this.registerViewStack(id, activeId, views, routerOutlet, this.props.location);
|
||||
if (!foundMatch) {
|
||||
const notFoundRoute = views.find(r => {
|
||||
// try to find a route that doesn't have a path or from prop, that will be our not found route
|
||||
return !r.routeData.childProps.path && !r.routeData.childProps.from;
|
||||
});
|
||||
if (notFoundRoute) {
|
||||
notFoundRoute.show = true;
|
||||
}
|
||||
}
|
||||
|
||||
function createViewItem(child: React.ReactElement<any>, location: HistoryLocation) {
|
||||
this.registerViewStack(id, activeId, views, routerOutlet, this.props.location);
|
||||
|
||||
function createViewItem(child: React.ReactElement<any>, routeId: string, location: HistoryLocation) {
|
||||
const viewId = generateId();
|
||||
const key = generateId();
|
||||
const route = child;
|
||||
|
||||
// const route = child;
|
||||
const matchProps = {
|
||||
exact: child.props.exact,
|
||||
path: child.props.path || child.props.from,
|
||||
@@ -170,7 +292,7 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
|
||||
match,
|
||||
childProps: child.props
|
||||
},
|
||||
route,
|
||||
routeId,
|
||||
mount: true,
|
||||
show: !!match,
|
||||
isIonRoute: false
|
||||
@@ -178,33 +300,56 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
|
||||
if (match && view.isIonRoute) {
|
||||
activeId = viewId;
|
||||
}
|
||||
if (!foundMatch && match) {
|
||||
foundMatch = true;
|
||||
}
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
async registerViewStack(stack: string, activeId: string | undefined, stackItems: ViewItem[], routerOutlet: HTMLIonRouterOutletElement, _location: HistoryLocation) {
|
||||
|
||||
return new Promise(resolve => {
|
||||
this.setState(prevState => {
|
||||
const prevViewStacks = Object.assign(new ViewStacks(), prevState.viewStacks);
|
||||
const newStack: ViewStack = {
|
||||
id: stack,
|
||||
views: stackItems,
|
||||
routerOutlet
|
||||
};
|
||||
if (activeId) {
|
||||
this.activeIonPageId = activeId;
|
||||
}
|
||||
prevViewStacks.set(stack, newStack);
|
||||
return {
|
||||
viewStacks: prevViewStacks
|
||||
};
|
||||
}, () => {
|
||||
resolve();
|
||||
});
|
||||
registerViewStack(stack: string, activeId: string | undefined, stackItems: ViewItem[], routerOutlet: HTMLIonRouterOutletElement, _location: HistoryLocation) {
|
||||
this.setState(prevState => {
|
||||
const prevViewStacks = Object.assign(new ViewStacks(), prevState.viewStacks);
|
||||
const newStack: ViewStack = {
|
||||
id: stack,
|
||||
views: stackItems
|
||||
};
|
||||
this.routerOutlets[stack] = routerOutlet;
|
||||
if (activeId) {
|
||||
this.activeIonPageId = activeId;
|
||||
}
|
||||
prevViewStacks.set(stack, newStack);
|
||||
return {
|
||||
viewStacks: prevViewStacks
|
||||
};
|
||||
}, () => {
|
||||
this.setupRouterOutlet(routerOutlet);
|
||||
});
|
||||
}
|
||||
|
||||
async setupRouterOutlet(routerOutlet: HTMLIonRouterOutletElement) {
|
||||
|
||||
const canStart = () => {
|
||||
const config = getConfig();
|
||||
const swipeEnabled = config && config.get('swipeBackEnabled', routerOutlet.mode === 'ios');
|
||||
if (swipeEnabled) {
|
||||
const { view } = this.state.viewStacks.findViewInfoById(this.activeIonPageId);
|
||||
return !!(view && view.prevId);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const onStart = () => {
|
||||
this.navigateBack();
|
||||
};
|
||||
routerOutlet.swipeHandler = {
|
||||
canStart,
|
||||
onStart,
|
||||
onEnd: _shouldContinue => true
|
||||
};
|
||||
}
|
||||
|
||||
removeViewStack(stack: string) {
|
||||
const viewStacks = Object.assign(new ViewStacks(), this.state.viewStacks);
|
||||
viewStacks.delete(stack);
|
||||
@@ -214,55 +359,124 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
|
||||
}
|
||||
|
||||
syncView(page: HTMLElement, viewId: string) {
|
||||
this.setState(state => {
|
||||
const viewStacks = Object.assign(new ViewStacks(), this.state.viewStacks);
|
||||
const { view } = viewStacks.findViewInfoById(viewId);
|
||||
if (view) {
|
||||
view.isIonRoute = true;
|
||||
this.ionPageElements[view.id] = page;
|
||||
this.setActiveView(this.state.location || this.props.location, this.state.action!, viewStacks);
|
||||
}
|
||||
}
|
||||
|
||||
const viewStacks = Object.assign(new ViewStacks(), state.viewStacks);
|
||||
const { view } = viewStacks.findViewInfoById(viewId);
|
||||
syncRoute(_id: string, routerOutlet: any) {
|
||||
const ionRouterOutlet = React.Children.only(routerOutlet) as React.ReactElement;
|
||||
|
||||
view!.ionPageElement = page;
|
||||
view!.isIonRoute = true;
|
||||
|
||||
return {
|
||||
viewStacks
|
||||
};
|
||||
|
||||
}, () => {
|
||||
this.setActiveView(this.state.location || this.props.location, this.state.action!);
|
||||
React.Children.forEach(ionRouterOutlet.props.children, (child: React.ReactElement) => {
|
||||
for (const routeKey in this.routes) {
|
||||
const route = this.routes[routeKey];
|
||||
if (typeof route.props.path !== 'undefined' && route.props.path === (child.props.path || child.props.from)) {
|
||||
this.routes[routeKey] = child;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
transitionView(enteringEl: HTMLElement, leavingEl: HTMLElement, ionRouterOutlet?: HTMLIonRouterOutletElement, direction?: NavDirection) {
|
||||
/**
|
||||
* Super hacky workaround to make sure ionRouterOutlet is available
|
||||
* since transitionView might be called before IonRouterOutlet is fully mounted
|
||||
*/
|
||||
if (ionRouterOutlet && ionRouterOutlet.componentOnReady) {
|
||||
this.commitView(enteringEl, leavingEl, ionRouterOutlet, direction);
|
||||
private async commitView(enteringEl: HTMLElement, leavingEl: HTMLElement, ionRouterOutlet: HTMLIonRouterOutletElement, direction?: NavDirection, showGoBack?: boolean, leavingViewHtml?: string) {
|
||||
if (!this.firstRender) {
|
||||
|
||||
if (!('componentOnReady' in ionRouterOutlet)) {
|
||||
await waitUntilRouterOutletReady(ionRouterOutlet);
|
||||
}
|
||||
|
||||
if ((enteringEl === leavingEl) && direction && leavingViewHtml) {
|
||||
// If a page is transitioning to another version of itself
|
||||
// we clone it so we can have an animation to show
|
||||
const newLeavingElement = clonePageElement(leavingViewHtml);
|
||||
ionRouterOutlet.appendChild(newLeavingElement);
|
||||
await ionRouterOutlet.commit(enteringEl, newLeavingElement, {
|
||||
deepWait: true,
|
||||
duration: direction === undefined ? 0 : undefined,
|
||||
direction,
|
||||
showGoBack,
|
||||
progressAnimation: false
|
||||
});
|
||||
ionRouterOutlet.removeChild(newLeavingElement);
|
||||
} else {
|
||||
await ionRouterOutlet.commit(enteringEl, leavingEl, {
|
||||
deepWait: true,
|
||||
duration: direction === undefined ? 0 : undefined,
|
||||
direction,
|
||||
showGoBack,
|
||||
progressAnimation: false
|
||||
});
|
||||
}
|
||||
|
||||
if (leavingEl && (enteringEl !== leavingEl)) {
|
||||
/** add hidden attributes */
|
||||
leavingEl.classList.add('ion-page-hidden');
|
||||
leavingEl.setAttribute('aria-hidden', 'true');
|
||||
}
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.transitionView(enteringEl, leavingEl, ionRouterOutlet, direction);
|
||||
}, 10);
|
||||
enteringEl.classList.remove('ion-page-invisible');
|
||||
enteringEl.style.zIndex = '101';
|
||||
enteringEl.dispatchEvent(new Event('ionViewWillEnter'));
|
||||
enteringEl.dispatchEvent(new Event('ionViewDidEnter'));
|
||||
this.firstRender = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async commitView(enteringEl: HTMLElement, leavingEl: HTMLElement, ionRouterOuter: HTMLIonRouterOutletElement, direction?: NavDirection) {
|
||||
|
||||
if (enteringEl === leavingEl) {
|
||||
return;
|
||||
handleNavigate(ionRouteAction: IonRouteAction, path: string, direction?: RouterDirection) {
|
||||
this.currentIonRouteAction = ionRouteAction;
|
||||
switch (ionRouteAction) {
|
||||
case 'push':
|
||||
this.currentRouteDirection = direction;
|
||||
this.props.history.push(path);
|
||||
break;
|
||||
case 'pop':
|
||||
this.currentRouteDirection = direction || 'back';
|
||||
this.props.history.replace(path);
|
||||
break;
|
||||
case 'replace':
|
||||
this.currentRouteDirection = 'none';
|
||||
this.props.history.replace(path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await ionRouterOuter.commit(enteringEl, leavingEl, {
|
||||
deepWait: true,
|
||||
duration: direction === undefined ? 0 : undefined,
|
||||
direction,
|
||||
showGoBack: direction === 'forward',
|
||||
progressAnimation: false
|
||||
});
|
||||
|
||||
if (leavingEl && (enteringEl !== leavingEl)) {
|
||||
/** add hidden attributes */
|
||||
leavingEl.classList.add('ion-page-hidden');
|
||||
leavingEl.setAttribute('aria-hidden', 'true');
|
||||
navigateBack(defaultHref?: string) {
|
||||
const { view: leavingView } = this.state.viewStacks.findViewInfoById(this.activeIonPageId);
|
||||
if (leavingView) {
|
||||
if (leavingView.id === leavingView.prevId) {
|
||||
const previousLocation = this.locationHistory.previous();
|
||||
if (previousLocation) {
|
||||
this.handleNavigate('pop', previousLocation.pathname + previousLocation.search);
|
||||
} else {
|
||||
defaultHref && this.handleNavigate('pop', defaultHref);
|
||||
}
|
||||
} else {
|
||||
const { view: enteringView } = this.state.viewStacks.findViewInfoById(leavingView.prevId);
|
||||
if (enteringView) {
|
||||
const lastLocation = this.locationHistory.findLastLocationByUrl(enteringView.routeData.match!.url);
|
||||
if (lastLocation) {
|
||||
this.handleNavigate('pop', lastLocation.pathname + lastLocation.search);
|
||||
} else {
|
||||
this.handleNavigate('pop', enteringView.routeData.match!.url);
|
||||
}
|
||||
} else {
|
||||
const currentLocation = this.locationHistory.previous();
|
||||
if (currentLocation) {
|
||||
this.handleNavigate('pop', currentLocation.pathname + currentLocation.search);
|
||||
} else {
|
||||
if (defaultHref) {
|
||||
this.handleNavigate('pop', defaultHref);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (defaultHref) {
|
||||
this.handleNavigate('replace', defaultHref, 'back');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,9 +485,8 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
|
||||
<RouteManagerContext.Provider value={this.state}>
|
||||
<NavManager
|
||||
{...this.props}
|
||||
findViewInfoById={(id: string) => this.state.viewStacks.findViewInfoById(id)}
|
||||
findViewInfoByLocation={(location: HistoryLocation) => this.state.viewStacks.findViewInfoByLocation(location)}
|
||||
getActiveIonPage={() => this.state.viewStacks.findViewInfoById(this.activeIonPageId)}
|
||||
onNavigateBack={this.navigateBack}
|
||||
onNavigate={this.handleNavigate}
|
||||
>
|
||||
{this.props.children}
|
||||
</NavManager>
|
||||
@@ -282,16 +495,28 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
|
||||
}
|
||||
}
|
||||
|
||||
const RouteManagerWithRouter = withRouter(RouteManager);
|
||||
RouteManagerWithRouter.displayName = 'RouteManager';
|
||||
function clonePageElement(leavingViewHtml: string) {
|
||||
const newEl = document.createElement('div');
|
||||
newEl.innerHTML = leavingViewHtml;
|
||||
newEl.classList.add('ion-page-hidden');
|
||||
newEl.style.zIndex = '';
|
||||
// Remove an existing back button so the new element doesn't get two of them
|
||||
const ionBackButton = newEl.getElementsByTagName('ion-back-button');
|
||||
if (ionBackButton[0]) {
|
||||
ionBackButton[0].innerHTML = '';
|
||||
}
|
||||
return newEl.firstChild as HTMLElement;
|
||||
}
|
||||
|
||||
export class IonReactRouter extends React.Component<BrowserRouterProps> {
|
||||
render() {
|
||||
const { children, ...props } = this.props;
|
||||
return (
|
||||
<BrowserRouter {...props}>
|
||||
<RouteManagerWithRouter>{children}</RouteManagerWithRouter>
|
||||
</BrowserRouter>
|
||||
);
|
||||
async function waitUntilRouterOutletReady(ionRouterOutlet: HTMLIonRouterElement) {
|
||||
if ('componentOnReady' in ionRouterOutlet) {
|
||||
return;
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
waitUntilRouterOutletReady(ionRouterOutlet);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
export const RouteManagerWithRouter = withRouter(RouteManager);
|
||||
RouteManagerWithRouter.displayName = 'RouteManager';
|
||||
|
||||
@@ -2,18 +2,21 @@ import React from 'react';
|
||||
|
||||
import { generateId, isDevMode } from '../utils';
|
||||
|
||||
import { RouteManagerContext } from './RouteManagerContext';
|
||||
import { RouteManagerContext, RouteManagerContextState } from './RouteManagerContext';
|
||||
import { View } from './View';
|
||||
import { ViewItem } from './ViewItem';
|
||||
import { ViewTransitionManager } from './ViewTransitionManager';
|
||||
|
||||
interface StackManagerProps {
|
||||
id?: string;
|
||||
routeManager: RouteManagerContextState;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export class StackManager extends React.Component<StackManagerProps, {}> {
|
||||
interface StackManagerState { }
|
||||
|
||||
class StackManagerInner extends React.Component<StackManagerProps, StackManagerState> {
|
||||
routerOutletEl: React.RefObject<HTMLIonRouterOutletElement> = React.createRef();
|
||||
context!: React.ContextType<typeof RouteManagerContext>;
|
||||
id: string;
|
||||
|
||||
constructor(props: StackManagerProps) {
|
||||
@@ -21,38 +24,44 @@ export class StackManager extends React.Component<StackManagerProps, {}> {
|
||||
this.id = this.props.id || generateId();
|
||||
this.handleViewSync = this.handleViewSync.bind(this);
|
||||
this.handleHideView = this.handleHideView.bind(this);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.context.setupIonRouter(this.id, this.props.children, this.routerOutletEl.current!);
|
||||
this.props.routeManager.setupIonRouter(this.id, this.props.children, this.routerOutletEl.current!);
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props: StackManagerProps, state: StackManagerState) {
|
||||
props.routeManager.syncRoute('', props.children);
|
||||
return state;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.context.removeViewStack(this.id);
|
||||
this.props.routeManager.removeViewStack(this.id);
|
||||
}
|
||||
|
||||
handleViewSync(page: HTMLElement, viewId: string) {
|
||||
this.context.syncView(page, viewId);
|
||||
this.props.routeManager.syncView(page, viewId);
|
||||
}
|
||||
|
||||
handleHideView(viewId: string) {
|
||||
this.context.hideView(viewId);
|
||||
this.props.routeManager.hideView(viewId);
|
||||
}
|
||||
|
||||
renderChild(item: ViewItem) {
|
||||
const component = React.cloneElement(item.route, {
|
||||
renderChild(item: ViewItem, route: any) {
|
||||
const component = React.cloneElement(route, {
|
||||
computedMatch: item.routeData.match
|
||||
});
|
||||
return component;
|
||||
}
|
||||
|
||||
render() {
|
||||
const context = this.context;
|
||||
const viewStack = context.viewStacks.get(this.id);
|
||||
const routeManager = this.props.routeManager;
|
||||
const viewStack = routeManager.viewStacks.get(this.id);
|
||||
const views = (viewStack || { views: [] }).views.filter(x => x.show);
|
||||
const ionRouterOutlet = React.Children.only(this.props.children) as React.ReactElement;
|
||||
|
||||
const childElements = views.map(view => {
|
||||
const route = routeManager.getRoute(view.routeId);
|
||||
return (
|
||||
<ViewTransitionManager
|
||||
id={view.id}
|
||||
@@ -63,8 +72,9 @@ export class StackManager extends React.Component<StackManagerProps, {}> {
|
||||
onViewSync={this.handleViewSync}
|
||||
onHideView={this.handleHideView}
|
||||
view={view}
|
||||
route={route}
|
||||
>
|
||||
{this.renderChild(view)}
|
||||
{this.renderChild(view, route)}
|
||||
</View>
|
||||
</ViewTransitionManager>
|
||||
);
|
||||
@@ -74,6 +84,10 @@ export class StackManager extends React.Component<StackManagerProps, {}> {
|
||||
ref: this.routerOutletEl
|
||||
};
|
||||
|
||||
if (ionRouterOutlet.props.forwardedRef) {
|
||||
ionRouterOutlet.props.forwardedRef.current = this.routerOutletEl;
|
||||
}
|
||||
|
||||
if (isDevMode()) {
|
||||
elementProps['data-stack-id'] = this.id;
|
||||
}
|
||||
@@ -82,8 +96,14 @@ export class StackManager extends React.Component<StackManagerProps, {}> {
|
||||
|
||||
return routerOutletChild;
|
||||
}
|
||||
|
||||
static get contextType() {
|
||||
return RouteManagerContext;
|
||||
}
|
||||
}
|
||||
|
||||
const withContext = (Component: any) => {
|
||||
return (props: any) => (
|
||||
<RouteManagerContext.Consumer>
|
||||
{context => <Component {...props} routeManager={context} />}
|
||||
</RouteManagerContext.Consumer>
|
||||
);
|
||||
};
|
||||
|
||||
export const StackManager = withContext(StackManagerInner);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { IonLifeCycleContext, NavContext } from '@ionic/react';
|
||||
import React from 'react';
|
||||
import { Redirect, Route } from 'react-router-dom';
|
||||
|
||||
import { isDevMode } from '../utils';
|
||||
|
||||
@@ -10,6 +9,7 @@ interface ViewProps extends React.HTMLAttributes<HTMLElement> {
|
||||
onViewSync: (page: HTMLElement, viewId: string) => void;
|
||||
onHideView: (viewId: string) => void;
|
||||
view: ViewItem;
|
||||
route: any;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -19,20 +19,6 @@ export class View extends React.Component<ViewProps, {}> {
|
||||
context!: React.ContextType<typeof IonLifeCycleContext>;
|
||||
ionPage?: HTMLElement;
|
||||
|
||||
componentDidMount() {
|
||||
/**
|
||||
* If we can tell if view is a redirect, hide it so it will work again in future
|
||||
*/
|
||||
const { view } = this.props;
|
||||
if (view.route.type === Redirect) {
|
||||
this.props.onHideView(view.id);
|
||||
} else if (view.route.type === Route && view.route.props.render) {
|
||||
if (view.route.props.render().type === Redirect) {
|
||||
this.props.onHideView(view.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.ionPage) {
|
||||
this.ionPage.removeEventListener('ionViewWillEnter', this.ionViewWillEnterHandler.bind(this));
|
||||
@@ -79,6 +65,7 @@ export class View extends React.Component<ViewProps, {}> {
|
||||
...value,
|
||||
registerIonPage: this.registerIonPage.bind(this)
|
||||
};
|
||||
|
||||
return (
|
||||
<NavContext.Provider value={newProvider}>
|
||||
{this.props.children}
|
||||
|
||||
@@ -3,10 +3,9 @@ export interface ViewItem<RouteData = any> {
|
||||
id: string;
|
||||
/** The key used by React. A new key is generated each time the view comes into the DOM so React thinks its a completely new element. */
|
||||
key: string;
|
||||
/** The <Route /> or <Redirect /> component associated with the view */
|
||||
route: React.ReactElement<any>;
|
||||
/** The reference to the <IonPage /> element. */
|
||||
ionPageElement?: HTMLElement;
|
||||
|
||||
routeId: string;
|
||||
|
||||
/** The routeData for the view. */
|
||||
routeData: RouteData;
|
||||
/** Used to track which page pushed the page into view. Used for back button purposes. */
|
||||
@@ -23,4 +22,9 @@ export interface ViewItem<RouteData = any> {
|
||||
* An IonRoute is a Route that contains an IonPage. Only IonPages participate in transition and lifecycle events.
|
||||
*/
|
||||
isIonRoute: boolean;
|
||||
|
||||
/**
|
||||
* location of the view
|
||||
*/
|
||||
location?: string;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import { ViewItem } from './ViewItem';
|
||||
|
||||
export interface ViewStack {
|
||||
id: string;
|
||||
routerOutlet: HTMLIonRouterOutletElement;
|
||||
views: ViewItem[];
|
||||
}
|
||||
|
||||
@@ -14,7 +13,7 @@ export interface ViewStack {
|
||||
* The holistic view of all the Routes configured for an application inside of an IonRouterOutlet.
|
||||
*/
|
||||
export class ViewStacks {
|
||||
private viewStacks: { [key: string]: ViewStack | undefined } = {};
|
||||
private viewStacks: { [key: string]: ViewStack | undefined; } = {};
|
||||
|
||||
get(key: string) {
|
||||
return this.viewStacks[key];
|
||||
@@ -32,25 +31,34 @@ export class ViewStacks {
|
||||
delete this.viewStacks[key];
|
||||
}
|
||||
|
||||
findViewInfoByLocation(location: HistoryLocation, viewKey?: string) {
|
||||
findViewInfoByLocation(location: HistoryLocation, viewKey: string) {
|
||||
let view: ViewItem<IonRouteData> | undefined;
|
||||
let match: IonRouteData['match'] | null | undefined;
|
||||
let viewStack: ViewStack | undefined;
|
||||
if (viewKey) {
|
||||
viewStack = this.viewStacks[viewKey];
|
||||
if (viewStack) {
|
||||
viewStack.views.some(matchView);
|
||||
|
||||
viewStack = this.viewStacks[viewKey];
|
||||
if (viewStack) {
|
||||
viewStack.views.some(matchView);
|
||||
|
||||
if (!view) {
|
||||
viewStack.views.some(r => {
|
||||
// try to find a route that doesn't have a path or from prop, that will be our not found route
|
||||
if (!r.routeData.childProps.path && !r.routeData.childProps.from) {
|
||||
match = {
|
||||
path: location.pathname,
|
||||
url: location.pathname,
|
||||
isExact: true,
|
||||
params: {}
|
||||
};
|
||||
view = r;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const keys = this.getKeys();
|
||||
keys.some(key => {
|
||||
viewStack = this.viewStacks[key];
|
||||
return viewStack!.views.some(matchView);
|
||||
});
|
||||
}
|
||||
|
||||
const result = { view, viewStack, match };
|
||||
return result;
|
||||
return { view, viewStack, match };
|
||||
|
||||
function matchView(v: ViewItem) {
|
||||
const matchProps = {
|
||||
@@ -58,9 +66,10 @@ export class ViewStacks {
|
||||
path: v.routeData.childProps.path || v.routeData.childProps.from,
|
||||
component: v.routeData.childProps.component
|
||||
};
|
||||
match = matchPath(location.pathname, matchProps);
|
||||
if (match) {
|
||||
const myMatch: IonRouteData['match'] | null | undefined = matchPath(location.pathname, matchProps);
|
||||
if (myMatch) {
|
||||
view = v;
|
||||
match = myMatch;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -85,16 +94,4 @@ export class ViewStacks {
|
||||
return { view, viewStack };
|
||||
}
|
||||
|
||||
setHiddenViews() {
|
||||
const keys = this.getKeys();
|
||||
keys.forEach(key => {
|
||||
const viewStack = this.viewStacks[key];
|
||||
viewStack!.views.forEach(view => {
|
||||
if (!view.routeData.match && !view.isIonRoute) {
|
||||
view.show = false;
|
||||
view.mount = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import { IonApp, IonRouterOutlet, IonPage } from '@ionic/react';
|
||||
import { IonReactRouter } from '../IonReactRouter';
|
||||
import { render } from '@testing-library/react';
|
||||
import { Route } from 'react-router';
|
||||
// import {Router} from '../Router';
|
||||
|
||||
describe('Router', () => {
|
||||
|
||||
|
||||
describe('on first page render', () => {
|
||||
|
||||
let IonTestApp: React.ComponentType<any>;
|
||||
|
||||
beforeEach(() => {
|
||||
IonTestApp = ({ Page }) => {
|
||||
return (
|
||||
<IonApp>
|
||||
<IonReactRouter>
|
||||
<IonRouterOutlet>
|
||||
<Route path="/" component={Page}></Route>
|
||||
</IonRouterOutlet>
|
||||
</IonReactRouter>
|
||||
</IonApp>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
it('should be visible', () => {
|
||||
|
||||
const MyPage = () => {
|
||||
return (
|
||||
<IonPage className="ion-page-invisible">
|
||||
<div>hello</div>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
const { container } = render(<IonTestApp Page={MyPage} />);
|
||||
const page = container.getElementsByClassName('ion-page')[0];
|
||||
expect(page).not.toHaveClass('ion-page-invisible');
|
||||
expect(page).toHaveStyle('z-index: 101')
|
||||
|
||||
});
|
||||
|
||||
it('should fire initial lifecycle events', () => {
|
||||
const ionViewWillEnterListener = jest.fn();
|
||||
const ionViewDidEnterListener = jest.fn();
|
||||
|
||||
const MyPage = () => {
|
||||
const ref = useRef<HTMLDivElement>();
|
||||
|
||||
useEffect(() => {
|
||||
ref.current.addEventListener('ionViewWillEnter', ionViewWillEnterListener);
|
||||
ref.current.addEventListener('ionViewDidEnter', ionViewDidEnterListener);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<IonPage ref={ref}>
|
||||
<div>hello</div>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
render(<IonTestApp Page={MyPage} />);
|
||||
expect(ionViewWillEnterListener).toHaveBeenCalledTimes(1);
|
||||
expect(ionViewDidEnterListener).toHaveBeenCalledTimes(1);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
});;
|
||||
@@ -1,2 +1,3 @@
|
||||
|
||||
export { IonReactRouter } from './Router';
|
||||
export { IonReactRouter } from './IonReactRouter';
|
||||
export { IonReactHashRouter } from './IonReactHashRouter';
|
||||
export { IonReactMemoryRouter } from './IonReactMemoryRouter';
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Location as HistoryLocation } from 'history';
|
||||
const RESTRICT_SIZE = 25;
|
||||
|
||||
export class LocationHistory {
|
||||
locationHistory: HistoryLocation[] = [];
|
||||
private locationHistory: HistoryLocation[] = [];
|
||||
|
||||
add(location: HistoryLocation) {
|
||||
this.locationHistory.push(location);
|
||||
@@ -12,9 +12,34 @@ export class LocationHistory {
|
||||
}
|
||||
}
|
||||
|
||||
findLastLocation(url: string) {
|
||||
const reversedLocations = [...this.locationHistory].reverse();
|
||||
const last = reversedLocations.find(x => x.pathname.toLowerCase() === url.toLowerCase());
|
||||
return last;
|
||||
pop() {
|
||||
this.locationHistory.pop();
|
||||
}
|
||||
|
||||
replace(location: HistoryLocation) {
|
||||
this.locationHistory.pop();
|
||||
this.locationHistory.push(location);
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.locationHistory = [];
|
||||
}
|
||||
|
||||
findLastLocationByUrl(url: string) {
|
||||
for (let i = this.locationHistory.length - 1; i >= 0; i--) {
|
||||
const location = this.locationHistory[i];
|
||||
if (location.pathname.toLocaleLowerCase() === url.toLocaleLowerCase()) {
|
||||
return location;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
previous() {
|
||||
return this.locationHistory[this.locationHistory.length - 2];
|
||||
}
|
||||
|
||||
current() {
|
||||
return this.locationHistory[this.locationHistory.length - 1];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"no-invalid-template-strings": true,
|
||||
"ban-export-const-enum": true,
|
||||
"only-arrow-functions": false,
|
||||
"strict-boolean-conditions": [true, "allow-null-union", "allow-undefined-union", "allow-boolean-or-undefined", "allow-string"],
|
||||
"strict-boolean-conditions": [false],
|
||||
"jsx-key": false,
|
||||
"jsx-self-close": false,
|
||||
"jsx-curly-spacing": [true, "never"],
|
||||
@@ -27,6 +27,11 @@
|
||||
"jsx-no-bind": false,
|
||||
"jsx-no-lambda": false,
|
||||
"jsx-no-multiline-js": false,
|
||||
"jsx-wrap-multiline": false
|
||||
"jsx-wrap-multiline": false,
|
||||
"forin": false,
|
||||
"strict-type-predicates": false,
|
||||
"no-unused-expression": false,
|
||||
"no-constant-condition": false,
|
||||
"no-empty-interface": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/react",
|
||||
"version": "4.11.0",
|
||||
"version": "4.11.13",
|
||||
"description": "React specific wrapper for @ionic/core",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -39,7 +39,7 @@
|
||||
"css/"
|
||||
],
|
||||
"dependencies": {
|
||||
"@ionic/core": "4.11.0",
|
||||
"@ionic/core": "4.11.13",
|
||||
"tslib": "*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -48,7 +48,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^23.3.9",
|
||||
"@types/node": "10.12.9",
|
||||
"@types/node": "^12.12.14",
|
||||
"@types/react": "^16.9.2",
|
||||
"@types/react-dom": "^16.9.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
@@ -66,7 +66,7 @@
|
||||
"tslint": "^5.18.0",
|
||||
"tslint-ionic-rules": "0.0.21",
|
||||
"tslint-react": "^4.0.0",
|
||||
"typescript": "3.5.3"
|
||||
"typescript": "^3.7.2"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "ts-jest",
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
import { ActionSheetOptions, actionSheetController } from '@ionic/core';
|
||||
import { ActionSheetButton as ActionSheetButtonCore, ActionSheetOptions as ActionSheetOptionsCore, actionSheetController as actionSheetControllerCore } from '@ionic/core';
|
||||
|
||||
import { createOverlayComponent } from './createOverlayComponent';
|
||||
|
||||
export interface ActionSheetButton extends Omit<ActionSheetButtonCore, 'icon'> {
|
||||
icon?: {
|
||||
ios: string;
|
||||
md: string;
|
||||
} | string;
|
||||
}
|
||||
|
||||
export interface ActionSheetOptions extends Omit<ActionSheetOptionsCore, 'buttons'> {
|
||||
buttons?: (ActionSheetButton | string)[];
|
||||
}
|
||||
|
||||
const actionSheetController = {
|
||||
create: (options: ActionSheetOptions) => actionSheetControllerCore.create(options as any),
|
||||
dismiss: (data?: any, role?: string | undefined, id?: string | undefined) => actionSheetControllerCore.dismiss(data, role, id),
|
||||
getTop: () => actionSheetControllerCore.getTop()
|
||||
};
|
||||
|
||||
export const IonActionSheet = /*@__PURE__*/createOverlayComponent<ActionSheetOptions, HTMLIonActionSheetElement>('IonActionSheet', actionSheetController);
|
||||
|
||||
83
packages/react/src/components/IonIcon.tsx
Normal file
83
packages/react/src/components/IonIcon.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
import React from 'react';
|
||||
|
||||
import { NavContext } from '../contexts/NavContext';
|
||||
|
||||
import { IonicReactProps } from './IonicReactProps';
|
||||
import { IonIconInner } from './inner-proxies';
|
||||
import { createForwardRef, isPlatform } from './utils';
|
||||
import { deprecationWarning } from './utils/dev';
|
||||
|
||||
interface IonIconProps {
|
||||
ariaLabel?: string;
|
||||
color?: string;
|
||||
flipRtl?: boolean;
|
||||
icon?: { ios: string; md: string; } | string;
|
||||
ios?: { ios: string; md: string; } | string;
|
||||
lazy?: boolean;
|
||||
md?: { ios: string; md: string; } | string;
|
||||
mode?: 'ios' | 'md';
|
||||
name?: string;
|
||||
size?: string;
|
||||
src?: string;
|
||||
}
|
||||
|
||||
type InternalProps = IonIconProps & {
|
||||
forwardedRef?: React.RefObject<HTMLIonIconElement>;
|
||||
};
|
||||
|
||||
class IonIconContainer extends React.PureComponent<InternalProps> {
|
||||
|
||||
constructor(props: InternalProps) {
|
||||
super(props);
|
||||
if (this.props.name) {
|
||||
deprecationWarning('icon-name', 'In Ionic React, you import icons from "ionicons/icons" and set the icon you imported to the "icon" property. Setting the "name" property has no effect.');
|
||||
}
|
||||
}
|
||||
|
||||
setIcon() {
|
||||
const { icon, ios, md } = this.props;
|
||||
if (ios || md) {
|
||||
if (isPlatform('ios')) {
|
||||
this.setState({
|
||||
icon: ios ?? md ?? icon
|
||||
});
|
||||
} else if (isPlatform('android')) {
|
||||
this.setState({
|
||||
icon: md ?? ios ?? icon
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.setState({
|
||||
icon
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { icon, ios, md, ...rest } = this.props;
|
||||
|
||||
let iconToUse: typeof icon;
|
||||
|
||||
if (ios || md) {
|
||||
if (isPlatform('ios')) {
|
||||
iconToUse = ios ?? md ?? icon;
|
||||
} else if (isPlatform('android')) {
|
||||
iconToUse = md ?? ios ?? icon;
|
||||
}
|
||||
} else {
|
||||
iconToUse = icon;
|
||||
}
|
||||
|
||||
return (
|
||||
<IonIconInner ref={this.props.forwardedRef} icon={iconToUse} {...rest}>
|
||||
{this.props.children}
|
||||
</IonIconInner>
|
||||
);
|
||||
}
|
||||
|
||||
static get contextType() {
|
||||
return NavContext;
|
||||
}
|
||||
}
|
||||
|
||||
export const IonIcon = createForwardRef<IonIconProps & IonicReactProps, HTMLIonIconElement>(IonIconContainer, 'IonIcon');
|
||||
@@ -3,13 +3,26 @@ import React from 'react';
|
||||
import { NavContext } from '../contexts/NavContext';
|
||||
|
||||
import { IonicReactProps } from './IonicReactProps';
|
||||
import { createForwardRef } from './utils';
|
||||
|
||||
export const IonPage = /*@__PURE__*/(() => class IonPageInternal extends React.Component<React.HTMLAttributes<HTMLElement> & IonicReactProps> {
|
||||
interface IonPageProps extends IonicReactProps {
|
||||
}
|
||||
|
||||
interface IonPageInternalProps extends IonPageProps {
|
||||
forwardedRef?: React.RefObject<HTMLDivElement>;
|
||||
}
|
||||
|
||||
class IonPageInternal extends React.Component<IonPageInternalProps> {
|
||||
context!: React.ContextType<typeof NavContext>;
|
||||
ref = React.createRef<HTMLDivElement>();
|
||||
ref: React.RefObject<HTMLDivElement>;
|
||||
|
||||
constructor(props: IonPageInternalProps) {
|
||||
super(props);
|
||||
this.ref = this.props.forwardedRef || React.createRef();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.context && this.ref.current) {
|
||||
if (this.context && this.ref && this.ref.current) {
|
||||
if (this.context.hasIonicRouter()) {
|
||||
this.context.registerIonPage(this.ref.current);
|
||||
}
|
||||
@@ -17,7 +30,7 @@ export const IonPage = /*@__PURE__*/(() => class IonPageInternal extends React.C
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, children, ...props } = this.props;
|
||||
const { className, children, forwardedRef, ...props } = this.props;
|
||||
|
||||
return (
|
||||
<div className={className ? `ion-page ${className}` : 'ion-page'} ref={this.ref} {...props}>
|
||||
@@ -33,4 +46,6 @@ export const IonPage = /*@__PURE__*/(() => class IonPageInternal extends React.C
|
||||
static get contextType() {
|
||||
return NavContext;
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
export const IonPage = createForwardRef(IonPageInternal, 'IonPage');
|
||||
|
||||
5
packages/react/src/components/IonPicker.tsx
Normal file
5
packages/react/src/components/IonPicker.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { PickerOptions, pickerController } from '@ionic/core';
|
||||
|
||||
import { createControllerComponent } from './createControllerComponent';
|
||||
|
||||
export const IonPicker = /*@__PURE__*/createControllerComponent<PickerOptions, HTMLIonPickerElement>('IonPicker', pickerController);
|
||||
@@ -13,7 +13,7 @@ type Props = LocalJSX.IonRouterOutlet & {
|
||||
};
|
||||
|
||||
type InternalProps = Props & {
|
||||
forwardedRef: any;
|
||||
forwardedRef?: React.RefObject<HTMLIonRouterOutletElement>;
|
||||
};
|
||||
|
||||
const IonRouterOutletContainer = /*@__PURE__*/(() => class extends React.Component<InternalProps> {
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
import { ToastOptions, toastController } from '@ionic/core';
|
||||
import { ToastButton as ToastButtonCore, ToastOptions as ToastOptionsCore, toastController as toastControllerCore } from '@ionic/core';
|
||||
|
||||
import { createControllerComponent } from './createControllerComponent';
|
||||
|
||||
export interface ToastButton extends Omit<ToastButtonCore, 'icon'> {
|
||||
icon?: {
|
||||
ios: string;
|
||||
md: string;
|
||||
} | string;
|
||||
}
|
||||
|
||||
export interface ToastOptions extends Omit<ToastOptionsCore, 'buttons'> {
|
||||
buttons?: (ToastButton | string)[];
|
||||
}
|
||||
|
||||
const toastController = {
|
||||
create: (options: ToastOptions) => toastControllerCore.create(options as any),
|
||||
dismiss: (data?: any, role?: string | undefined, id?: string | undefined) => toastControllerCore.dismiss(data, role, id),
|
||||
getTop: () => toastControllerCore.getTop()
|
||||
};
|
||||
|
||||
export const IonToast = /*@__PURE__*/createControllerComponent<ToastOptions, HTMLIonToastElement>('IonToast', toastController);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
export interface IonicReactProps {
|
||||
class?: string;
|
||||
className?: string;
|
||||
style?: {[key: string]: any };
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ describe('IonTabs', () => {
|
||||
const { container } = render(
|
||||
<IonTabs>
|
||||
<IonRouterOutlet></IonRouterOutlet>
|
||||
<IonTabBar slot="bottom" currentPath={'/'} navigate={() => {}}>
|
||||
<IonTabBar slot="bottom" currentPath={'/'}>
|
||||
<IonTabButton tab="schedule">
|
||||
<IonLabel>Schedule</IonLabel>
|
||||
<IonIcon name="schedule"></IonIcon>
|
||||
@@ -44,7 +44,7 @@ describe('IonTabs', () => {
|
||||
const { container } = render(
|
||||
<IonTabs>
|
||||
<IonRouterOutlet></IonRouterOutlet>
|
||||
<IonTabBar slot="bottom" currentPath={'/'} navigate={() => {}}>
|
||||
<IonTabBar slot="bottom" currentPath={'/'}>
|
||||
{false &&
|
||||
<IonTabButton tab="schedule">
|
||||
<IonLabel>Schedule</IonLabel>
|
||||
|
||||
@@ -11,21 +11,31 @@ interface OverlayBase extends HTMLElement {
|
||||
export interface ReactControllerProps {
|
||||
isOpen: boolean;
|
||||
onDidDismiss?: (event: CustomEvent<OverlayEventDetail>) => void;
|
||||
onDidPresent?: (event: CustomEvent<OverlayEventDetail>) => void;
|
||||
onWillDismiss?: (event: CustomEvent<OverlayEventDetail>) => void;
|
||||
onWillPresent?: (event: CustomEvent<OverlayEventDetail>) => void;
|
||||
}
|
||||
|
||||
export const createControllerComponent = <OptionsType extends object, OverlayType extends OverlayBase>(
|
||||
displayName: string,
|
||||
controller: { create: (options: OptionsType) => Promise<OverlayType> }
|
||||
controller: { create: (options: OptionsType) => Promise<OverlayType>; }
|
||||
) => {
|
||||
const dismissEventName = `on${displayName}DidDismiss`;
|
||||
const didDismissEventName = `on${displayName}DidDismiss`;
|
||||
const didPresentEventName = `on${displayName}DidPresent`;
|
||||
const willDismissEventName = `on${displayName}WillDismiss`;
|
||||
const willPresentEventName = `on${displayName}WillPresent`;
|
||||
|
||||
type Props = OptionsType & ReactControllerProps;
|
||||
type Props = OptionsType & ReactControllerProps & {
|
||||
forwardedRef?: React.RefObject<OverlayType>;
|
||||
};
|
||||
|
||||
return class extends React.Component<Props> {
|
||||
class Overlay extends React.Component<Props> {
|
||||
overlay?: OverlayType;
|
||||
isUnmounted = false;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.handleDismiss = this.handleDismiss.bind(this);
|
||||
}
|
||||
|
||||
static get displayName() {
|
||||
@@ -40,6 +50,7 @@ export const createControllerComponent = <OptionsType extends object, OverlayTyp
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isUnmounted = true;
|
||||
if (this.overlay) { this.overlay.dismiss(); }
|
||||
}
|
||||
|
||||
@@ -52,19 +63,42 @@ export const createControllerComponent = <OptionsType extends object, OverlayTyp
|
||||
}
|
||||
}
|
||||
|
||||
handleDismiss(event: CustomEvent<OverlayEventDetail<any>>) {
|
||||
if (this.props.onDidDismiss) {
|
||||
this.props.onDidDismiss(event);
|
||||
}
|
||||
if (this.props.forwardedRef) {
|
||||
(this.props.forwardedRef as any).current = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
async present(prevProps?: Props) {
|
||||
const { isOpen, onDidDismiss, ...cProps } = this.props;
|
||||
const overlay = this.overlay = await controller.create({
|
||||
const { isOpen, onDidDismiss, onDidPresent, onWillDismiss, onWillPresent, ...cProps } = this.props;
|
||||
this.overlay = await controller.create({
|
||||
...cProps as any
|
||||
});
|
||||
attachProps(overlay, {
|
||||
[dismissEventName]: onDidDismiss
|
||||
attachProps(this.overlay, {
|
||||
[didDismissEventName]: this.handleDismiss,
|
||||
[didPresentEventName]: (e: CustomEvent) => this.props.onDidPresent && this.props.onDidPresent(e),
|
||||
[willDismissEventName]: (e: CustomEvent) => this.props.onWillDismiss && this.props.onWillDismiss(e),
|
||||
[willPresentEventName]: (e: CustomEvent) => this.props.onWillPresent && this.props.onWillPresent(e)
|
||||
}, prevProps);
|
||||
await overlay.present();
|
||||
// Check isOpen again since the value could have changed during the async call to controller.create
|
||||
// It's also possible for the component to have become unmounted.
|
||||
if (this.props.isOpen === true && this.isUnmounted === false) {
|
||||
if (this.props.forwardedRef) {
|
||||
(this.props.forwardedRef as any).current = this.overlay;
|
||||
}
|
||||
await this.overlay.present();
|
||||
}
|
||||
}
|
||||
|
||||
render(): null {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return React.forwardRef<OverlayType, Props>((props, ref) => {
|
||||
return <Overlay {...props} forwardedRef={ref} />;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -13,23 +13,32 @@ export interface ReactOverlayProps {
|
||||
children?: React.ReactNode;
|
||||
isOpen: boolean;
|
||||
onDidDismiss?: (event: CustomEvent<OverlayEventDetail>) => void;
|
||||
onDidPresent?: (event: CustomEvent<OverlayEventDetail>) => void;
|
||||
onWillDismiss?: (event: CustomEvent<OverlayEventDetail>) => void;
|
||||
onWillPresent?: (event: CustomEvent<OverlayEventDetail>) => void;
|
||||
}
|
||||
|
||||
export const createOverlayComponent = <T extends object, OverlayType extends OverlayElement>(
|
||||
export const createOverlayComponent = <OverlayComponent extends object, OverlayType extends OverlayElement>(
|
||||
displayName: string,
|
||||
controller: { create: (options: any) => Promise<OverlayType> }
|
||||
controller: { create: (options: any) => Promise<OverlayType>; }
|
||||
) => {
|
||||
const dismissEventName = `on${displayName}DidDismiss`;
|
||||
const didDismissEventName = `on${displayName}DidDismiss`;
|
||||
const didPresentEventName = `on${displayName}DidPresent`;
|
||||
const willDismissEventName = `on${displayName}WillDismiss`;
|
||||
const willPresentEventName = `on${displayName}WillPresent`;
|
||||
|
||||
type Props = T & ReactOverlayProps;
|
||||
type Props = OverlayComponent & ReactOverlayProps & {
|
||||
forwardedRef?: React.RefObject<OverlayType>;
|
||||
};
|
||||
|
||||
return class extends React.Component<Props> {
|
||||
class Overlay extends React.Component<Props> {
|
||||
overlay?: OverlayType;
|
||||
el: HTMLDivElement;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.el = document.createElement('div');
|
||||
this.handleDismiss = this.handleDismiss.bind(this);
|
||||
}
|
||||
|
||||
static get displayName() {
|
||||
@@ -37,7 +46,7 @@ export const createOverlayComponent = <T extends object, OverlayType extends Ove
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.isOpen as boolean) {
|
||||
if (this.props.isOpen) {
|
||||
this.present();
|
||||
}
|
||||
}
|
||||
@@ -46,7 +55,20 @@ export const createOverlayComponent = <T extends object, OverlayType extends Ove
|
||||
if (this.overlay) { this.overlay.dismiss(); }
|
||||
}
|
||||
|
||||
handleDismiss(event: CustomEvent<OverlayEventDetail<any>>) {
|
||||
if (this.props.onDidDismiss) {
|
||||
this.props.onDidDismiss(event);
|
||||
}
|
||||
if (this.props.forwardedRef) {
|
||||
(this.props.forwardedRef as any).current = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
async componentDidUpdate(prevProps: Props) {
|
||||
if (this.overlay) {
|
||||
attachProps(this.overlay, this.props, prevProps);
|
||||
}
|
||||
|
||||
if (prevProps.isOpen !== this.props.isOpen && this.props.isOpen === true) {
|
||||
this.present(prevProps);
|
||||
}
|
||||
@@ -56,28 +78,40 @@ export const createOverlayComponent = <T extends object, OverlayType extends Ove
|
||||
}
|
||||
|
||||
async present(prevProps?: Props) {
|
||||
const { children, isOpen, onDidDismiss = () => { return; }, ...cProps } = this.props;
|
||||
const { children, isOpen, onDidDismiss, onDidPresent, onWillDismiss, onWillPresent, ...cProps } = this.props;
|
||||
const elementProps = {
|
||||
...cProps,
|
||||
[dismissEventName]: onDidDismiss
|
||||
ref: this.props.forwardedRef,
|
||||
[didDismissEventName]: this.handleDismiss,
|
||||
[didPresentEventName]: (e: CustomEvent) => this.props.onDidPresent && this.props.onDidPresent(e),
|
||||
[willDismissEventName]: (e: CustomEvent) => this.props.onWillDismiss && this.props.onWillDismiss(e),
|
||||
[willPresentEventName]: (e: CustomEvent) => this.props.onWillPresent && this.props.onWillPresent(e)
|
||||
};
|
||||
|
||||
const overlay = this.overlay = await controller.create({
|
||||
this.overlay = await controller.create({
|
||||
...elementProps,
|
||||
component: this.el,
|
||||
componentProps: {}
|
||||
});
|
||||
|
||||
attachProps(overlay, elementProps, prevProps);
|
||||
if (this.props.forwardedRef) {
|
||||
(this.props.forwardedRef as any).current = this.overlay;
|
||||
}
|
||||
|
||||
await overlay.present();
|
||||
attachProps(this.overlay, elementProps, prevProps);
|
||||
|
||||
await this.overlay.present();
|
||||
}
|
||||
|
||||
render() {
|
||||
return ReactDOM.createPortal(
|
||||
this.props.children,
|
||||
this.el,
|
||||
this.props.isOpen ? this.props.children : null,
|
||||
this.el
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return React.forwardRef<OverlayType, Props>((props, ref) => {
|
||||
return <Overlay {...props} forwardedRef={ref} />;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export declare type RouterDirection = 'forward' | 'back' | 'none';
|
||||
export declare type RouterDirection = 'forward' | 'back' | 'root' | 'none';
|
||||
|
||||
export type HrefProps<T> = Omit<T, 'routerDirection'> & {
|
||||
routerLink?: string;
|
||||
|
||||
@@ -8,10 +8,11 @@ export * from './proxies';
|
||||
// createControllerComponent
|
||||
export { IonAlert } from './IonAlert';
|
||||
export { IonLoading } from './IonLoading';
|
||||
export { IonToast } from './IonToast';
|
||||
export * from './IonToast';
|
||||
export { IonPicker } from './IonPicker';
|
||||
|
||||
// createOverlayComponent
|
||||
export { IonActionSheet } from './IonActionSheet';
|
||||
export * from './IonActionSheet';
|
||||
export { IonModal } from './IonModal';
|
||||
export { IonPopover } from './IonPopover';
|
||||
|
||||
@@ -21,9 +22,10 @@ export { IonTabs } from './navigation/IonTabs';
|
||||
export { IonTabBar } from './navigation/IonTabBar';
|
||||
export { IonBackButton } from './navigation/IonBackButton';
|
||||
export { IonRouterOutlet } from './IonRouterOutlet';
|
||||
export { IonIcon } from './IonIcon';
|
||||
|
||||
// Utils
|
||||
export { isPlatform, getPlatforms } from './utils';
|
||||
export { isPlatform, getPlatforms, getConfig } from './utils';
|
||||
export { RouterDirection } from './hrefprops';
|
||||
|
||||
// Icons that are used by internal components
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { JSX } from '@ionic/core';
|
||||
import { JSX as IoniconsJSX } from 'ionicons';
|
||||
|
||||
import { /*@__PURE__*/ createReactComponent } from './createComponent';
|
||||
|
||||
export const IonTabBarInner = /*@__PURE__*/createReactComponent<JSX.IonTabBar, HTMLIonTabBarElement>('ion-tab-bar');
|
||||
export const IonBackButtonInner = /*@__PURE__*/createReactComponent<Omit<JSX.IonBackButton, 'icon'>, HTMLIonBackButtonElement>('ion-back-button');
|
||||
export const IonRouterOutletInner = /*@__PURE__*/createReactComponent<JSX.IonRouterOutlet, HTMLIonRouterOutletElement>('ion-router-outlet');
|
||||
|
||||
// ionicons
|
||||
export const IonIconInner = /*@__PURE__*/createReactComponent<IoniconsJSX.IonIcon, HTMLIonIconElement>('ion-icon');
|
||||
|
||||
@@ -9,7 +9,7 @@ type Props = Omit<LocalJSX.IonBackButton, 'icon'> & IonicReactProps & {
|
||||
icon?: {
|
||||
ios: string;
|
||||
md: string;
|
||||
};
|
||||
} | string;
|
||||
ref?: React.RefObject<HTMLIonBackButtonElement>;
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@ import { IonTabBarInner } from '../inner-proxies';
|
||||
import { IonTabButton } from '../proxies';
|
||||
|
||||
type Props = LocalJSX.IonTabBar & {
|
||||
navigate?: (path: string, direction: 'back' | 'none') => void;
|
||||
onIonTabsDidChange?: (event: CustomEvent<{ tab: string }>) => void;
|
||||
onIonTabsWillChange?: (event: CustomEvent<{ tab: string }>) => void;
|
||||
currentPath?: string;
|
||||
slot?: 'bottom' | 'top';
|
||||
};
|
||||
@@ -22,6 +23,7 @@ interface State {
|
||||
}
|
||||
|
||||
const IonTabBarUnwrapped = /*@__PURE__*/(() => class extends React.Component<Props, State> {
|
||||
context!: React.ContextType<typeof NavContext>;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
@@ -67,13 +69,22 @@ const IonTabBarUnwrapped = /*@__PURE__*/(() => class extends React.Component<Pro
|
||||
}
|
||||
|
||||
private onTabButtonClick = (e: CustomEvent<{ href: string, selected: boolean, tab: string }>) => {
|
||||
const { navigate } = this.props;
|
||||
if (navigate) {
|
||||
if (this.state.activeTab === e.detail.tab) {
|
||||
navigate(this.state.tabs[e.detail.tab].originalHref, 'back');
|
||||
const originalHref = this.state.tabs[e.detail.tab].originalHref;
|
||||
const currentHref = this.state.tabs[e.detail.tab].currentHref;
|
||||
if (this.state.activeTab === e.detail.tab) {
|
||||
if (originalHref === currentHref) {
|
||||
this.context.navigate(originalHref, 'none');
|
||||
} else {
|
||||
navigate(this.state.tabs[e.detail.tab].currentHref, 'none');
|
||||
this.context.navigate(originalHref, 'back', 'pop');
|
||||
}
|
||||
} else {
|
||||
if (this.props.onIonTabsWillChange) {
|
||||
this.props.onIonTabsWillChange(new CustomEvent('ionTabWillChange', { detail: { tab: e.detail.tab } }));
|
||||
}
|
||||
if (this.props.onIonTabsDidChange) {
|
||||
this.props.onIonTabsDidChange(new CustomEvent('ionTabDidChange', { detail: { tab: e.detail.tab } }));
|
||||
}
|
||||
this.context.navigate(currentHref, 'none');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +107,10 @@ const IonTabBarUnwrapped = /*@__PURE__*/(() => class extends React.Component<Pro
|
||||
</IonTabBarInner>
|
||||
);
|
||||
}
|
||||
|
||||
static get contextType() {
|
||||
return NavContext;
|
||||
}
|
||||
})();
|
||||
|
||||
export const IonTabBar: React.FC<Props> = props => {
|
||||
@@ -103,9 +118,6 @@ export const IonTabBar: React.FC<Props> = props => {
|
||||
return (
|
||||
<IonTabBarUnwrapped
|
||||
{...props as any}
|
||||
navigate={props.navigate || ((path: string, direction: 'back' | 'none') => {
|
||||
context.navigate(path, direction);
|
||||
})}
|
||||
currentPath={props.currentPath || context.currentPath}
|
||||
>
|
||||
{props.children}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { JSX as LocalJSX } from '@ionic/core';
|
||||
import React from 'react';
|
||||
|
||||
import { NavContext } from '../../contexts/NavContext';
|
||||
@@ -5,7 +6,7 @@ import { IonRouterOutlet } from '../IonRouterOutlet';
|
||||
|
||||
import { IonTabBar } from './IonTabBar';
|
||||
|
||||
interface Props {
|
||||
interface Props extends LocalJSX.IonTabs {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
@@ -28,7 +29,7 @@ const tabsInner: React.CSSProperties = {
|
||||
contain: 'layout size style'
|
||||
};
|
||||
|
||||
export const IonTabs = /*@__PURE__*/(() => class extends React.Component<Props> {
|
||||
export class IonTabs extends React.Component<Props> {
|
||||
context!: React.ContextType<typeof NavContext>;
|
||||
routerOutletRef: React.Ref<HTMLIonRouterOutletElement> = React.createRef();
|
||||
|
||||
@@ -38,7 +39,7 @@ export const IonTabs = /*@__PURE__*/(() => class extends React.Component<Props>
|
||||
|
||||
render() {
|
||||
let outlet: React.ReactElement<{}> | undefined;
|
||||
let tabBar: React.ReactElement<{ slot: 'bottom' | 'top' }> | undefined;
|
||||
let tabBar: React.ReactElement | undefined;
|
||||
|
||||
React.Children.forEach(this.props.children, (child: any) => {
|
||||
if (child == null || typeof child !== 'object' || !child.hasOwnProperty('type')) {
|
||||
@@ -48,7 +49,8 @@ export const IonTabs = /*@__PURE__*/(() => class extends React.Component<Props>
|
||||
outlet = child;
|
||||
}
|
||||
if (child.type === IonTabBar) {
|
||||
tabBar = child;
|
||||
const { onIonTabsDidChange, onIonTabsWillChange } = this.props;
|
||||
tabBar = React.cloneElement(child, { onIonTabsDidChange, onIonTabsWillChange });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -71,11 +73,7 @@ export const IonTabs = /*@__PURE__*/(() => class extends React.Component<Props>
|
||||
);
|
||||
}
|
||||
|
||||
static get displayName() {
|
||||
return 'IonTabs';
|
||||
}
|
||||
|
||||
static get contextType() {
|
||||
return NavContext;
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import { JSX } from '@ionic/core';
|
||||
import { JSX as IoniconsJSX } from 'ionicons';
|
||||
|
||||
import { createReactComponent } from './createComponent';
|
||||
import { HrefProps } from './hrefprops';
|
||||
|
||||
// ionicons
|
||||
export const IonIcon = /*@__PURE__*/createReactComponent<IoniconsJSX.IonIcon, HTMLIonIconElement>('ion-icon');
|
||||
|
||||
// ionic/core
|
||||
export const IonApp = /*@__PURE__*/createReactComponent<JSX.IonApp, HTMLIonAppElement>('ion-app');
|
||||
export const IonTab = /*@__PURE__*/createReactComponent<JSX.IonTab, HTMLIonTabElement>('ion-tab');
|
||||
@@ -50,7 +46,6 @@ export const IonMenu = /*@__PURE__*/createReactComponent<JSX.IonMenu, HTMLIonMen
|
||||
export const IonMenuButton = /*@__PURE__*/createReactComponent<JSX.IonMenuButton, HTMLIonMenuButtonElement>('ion-menu-button');
|
||||
export const IonMenuToggle = /*@__PURE__*/createReactComponent<JSX.IonMenuToggle, HTMLIonMenuToggleElement>('ion-menu-toggle');
|
||||
export const IonNote = /*@__PURE__*/createReactComponent<JSX.IonNote, HTMLIonNoteElement>('ion-note');
|
||||
export const IonPicker = /*@__PURE__*/createReactComponent<JSX.IonPicker, HTMLIonPickerElement>('ion-picker');
|
||||
export const IonPickerColumn = /*@__PURE__*/createReactComponent<JSX.IonPickerColumn, HTMLIonPickerColumnElement>('ion-picker-column');
|
||||
export const IonNav = /*@__PURE__*/createReactComponent<JSX.IonNav, HTMLIonNavElement>('ion-nav');
|
||||
export const IonProgressBar = /*@__PURE__*/createReactComponent<JSX.IonProgressBar, HTMLIonProgressBarElement>('ion-progress-bar');
|
||||
|
||||
@@ -1,33 +1,36 @@
|
||||
import { camelToDashCase } from './case';
|
||||
|
||||
export const attachProps = (node: HTMLElement, newProps: any, oldProps: any = {}) => {
|
||||
// add any classes in className to the class list
|
||||
const className = getClassName(node.classList, newProps, oldProps);
|
||||
if (className !== '') {
|
||||
node.className = className;
|
||||
}
|
||||
|
||||
Object.keys(newProps).forEach(name => {
|
||||
if (name === 'children' || name === 'style' || name === 'ref' || name === 'class' || name === 'className' || name === 'forwardedRef') {
|
||||
return;
|
||||
// some test frameworks don't render DOM elements, so we test here to make sure we are dealing with DOM first
|
||||
if (node instanceof Element) {
|
||||
// add any classes in className to the class list
|
||||
const className = getClassName(node.classList, newProps, oldProps);
|
||||
if (className !== '') {
|
||||
node.className = className;
|
||||
}
|
||||
if (name.indexOf('on') === 0 && name[2] === name[2].toUpperCase()) {
|
||||
const eventName = name.substring(2);
|
||||
const eventNameLc = eventName[0].toLowerCase() + eventName.substring(1);
|
||||
|
||||
if (!isCoveredByReact(eventNameLc)) {
|
||||
syncEvent(node, eventNameLc, newProps[name]);
|
||||
Object.keys(newProps).forEach(name => {
|
||||
if (name === 'children' || name === 'style' || name === 'ref' || name === 'class' || name === 'className' || name === 'forwardedRef') {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
(node as any)[name] = newProps[name];
|
||||
const propType = typeof newProps[name];
|
||||
if (propType === 'string') {
|
||||
node.setAttribute(camelToDashCase(name), newProps[name]);
|
||||
if (name.indexOf('on') === 0 && name[2] === name[2].toUpperCase()) {
|
||||
const eventName = name.substring(2);
|
||||
const eventNameLc = eventName[0].toLowerCase() + eventName.substring(1);
|
||||
|
||||
if (!isCoveredByReact(eventNameLc)) {
|
||||
syncEvent(node, eventNameLc, newProps[name]);
|
||||
}
|
||||
} else {
|
||||
(node as any)[name] = newProps[name];
|
||||
const propType = typeof newProps[name];
|
||||
if (propType === 'string') {
|
||||
node.setAttribute(camelToDashCase(name), newProps[name]);
|
||||
} else {
|
||||
(node as any)[name] = newProps[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const getClassName = (classList: DOMTokenList, newProps: any, oldProps: any) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Platforms, getPlatforms as getPlatformsCore, isPlatform as isPlatformCore } from '@ionic/core';
|
||||
import { Config as CoreConfig, Platforms, getPlatforms as getPlatformsCore, isPlatform as isPlatformCore } from '@ionic/core';
|
||||
import React from 'react';
|
||||
|
||||
import { IonicReactProps } from '../IonicReactProps';
|
||||
@@ -24,3 +24,13 @@ export const isPlatform = (platform: Platforms) => {
|
||||
export const getPlatforms = () => {
|
||||
return getPlatformsCore(window);
|
||||
};
|
||||
|
||||
export const getConfig = (): CoreConfig | null => {
|
||||
if (typeof (window as any) !== 'undefined') {
|
||||
const Ionic = (window as any).Ionic;
|
||||
if (Ionic && Ionic.config) {
|
||||
return Ionic.config;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -22,52 +22,82 @@ export const IonLifeCycleContext = /*@__PURE__*/React.createContext<IonLifeCycle
|
||||
ionViewDidLeave: () => { return; },
|
||||
});
|
||||
|
||||
export interface LifeCycleCallback { (): void; id?: number; }
|
||||
|
||||
export const DefaultIonLifeCycleContext = class implements IonLifeCycleContextInterface {
|
||||
|
||||
ionViewWillEnterCallback?: () => void;
|
||||
ionViewDidEnterCallback?: () => void;
|
||||
ionViewWillLeaveCallback?: () => void;
|
||||
ionViewDidLeaveCallback?: () => void;
|
||||
ionViewWillEnterCallbacks: LifeCycleCallback[] = [];
|
||||
ionViewDidEnterCallbacks: LifeCycleCallback[] = [];
|
||||
ionViewWillLeaveCallbacks: LifeCycleCallback[] = [];
|
||||
ionViewDidLeaveCallbacks: LifeCycleCallback[] = [];
|
||||
componentCanBeDestroyedCallback?: () => void;
|
||||
|
||||
onIonViewWillEnter(callback: () => void) {
|
||||
this.ionViewWillEnterCallback = callback;
|
||||
onIonViewWillEnter(callback: LifeCycleCallback) {
|
||||
if (callback.id) {
|
||||
const index = this.ionViewWillEnterCallbacks.findIndex(x => x.id === callback.id);
|
||||
if (index > -1) {
|
||||
this.ionViewWillEnterCallbacks[index] = callback;
|
||||
} else {
|
||||
this.ionViewWillEnterCallbacks.push(callback);
|
||||
}
|
||||
} else {
|
||||
this.ionViewWillEnterCallbacks.push(callback);
|
||||
}
|
||||
}
|
||||
|
||||
ionViewWillEnter() {
|
||||
if (this.ionViewWillEnterCallback) {
|
||||
this.ionViewWillEnterCallback();
|
||||
}
|
||||
this.ionViewWillEnterCallbacks.forEach(cb => cb());
|
||||
}
|
||||
|
||||
onIonViewDidEnter(callback: () => void) {
|
||||
this.ionViewDidEnterCallback = callback;
|
||||
onIonViewDidEnter(callback: LifeCycleCallback) {
|
||||
if (callback.id) {
|
||||
const index = this.ionViewDidEnterCallbacks.findIndex(x => x.id === callback.id);
|
||||
if (index > -1) {
|
||||
this.ionViewDidEnterCallbacks[index] = callback;
|
||||
} else {
|
||||
this.ionViewDidEnterCallbacks.push(callback);
|
||||
}
|
||||
} else {
|
||||
this.ionViewDidEnterCallbacks.push(callback);
|
||||
}
|
||||
}
|
||||
|
||||
ionViewDidEnter() {
|
||||
if (this.ionViewDidEnterCallback) {
|
||||
this.ionViewDidEnterCallback();
|
||||
}
|
||||
this.ionViewDidEnterCallbacks.forEach(cb => cb());
|
||||
}
|
||||
|
||||
onIonViewWillLeave(callback: () => void) {
|
||||
this.ionViewWillLeaveCallback = callback;
|
||||
onIonViewWillLeave(callback: LifeCycleCallback) {
|
||||
if (callback.id) {
|
||||
const index = this.ionViewWillLeaveCallbacks.findIndex(x => x.id === callback.id);
|
||||
if (index > -1) {
|
||||
this.ionViewWillLeaveCallbacks[index] = callback;
|
||||
} else {
|
||||
this.ionViewWillLeaveCallbacks.push(callback);
|
||||
}
|
||||
} else {
|
||||
this.ionViewWillLeaveCallbacks.push(callback);
|
||||
}
|
||||
}
|
||||
|
||||
ionViewWillLeave() {
|
||||
if (this.ionViewWillLeaveCallback) {
|
||||
this.ionViewWillLeaveCallback();
|
||||
}
|
||||
this.ionViewWillLeaveCallbacks.forEach(cb => cb());
|
||||
}
|
||||
|
||||
onIonViewDidLeave(callback: () => void) {
|
||||
this.ionViewDidLeaveCallback = callback;
|
||||
onIonViewDidLeave(callback: LifeCycleCallback) {
|
||||
if (callback.id) {
|
||||
const index = this.ionViewDidLeaveCallbacks.findIndex(x => x.id === callback.id);
|
||||
if (index > -1) {
|
||||
this.ionViewDidLeaveCallbacks[index] = callback;
|
||||
} else {
|
||||
this.ionViewDidLeaveCallbacks.push(callback);
|
||||
}
|
||||
} else {
|
||||
this.ionViewDidLeaveCallbacks.push(callback);
|
||||
}
|
||||
}
|
||||
|
||||
ionViewDidLeave() {
|
||||
if (this.ionViewDidLeaveCallback) {
|
||||
this.ionViewDidLeaveCallback();
|
||||
}
|
||||
this.ionViewDidLeaveCallbacks.forEach(cb => cb());
|
||||
this.componentCanBeDestroyed();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,20 +2,16 @@ import { RouterDirection } from '@ionic/core';
|
||||
import React from 'react';
|
||||
|
||||
export interface NavContextState {
|
||||
getHistory: () => History;
|
||||
getLocation: () => Location;
|
||||
getPageManager: () => any;
|
||||
getStackManager: () => any;
|
||||
goBack: (defaultHref?: string) => void;
|
||||
navigate: (path: string, direction?: RouterDirection | 'none') => void;
|
||||
navigate: (path: string, direction?: RouterDirection | 'none', ionRouteAction?: 'push' | 'replace' | 'pop') => void;
|
||||
hasIonicRouter: () => boolean;
|
||||
registerIonPage: (page: HTMLElement) => void;
|
||||
currentPath: string | undefined;
|
||||
}
|
||||
|
||||
export const NavContext = /*@__PURE__*/React.createContext<NavContextState>({
|
||||
getHistory: () => window.history,
|
||||
getLocation: () => window.location,
|
||||
getPageManager: () => undefined,
|
||||
getStackManager: () => undefined,
|
||||
goBack: (defaultHref?: string) => {
|
||||
|
||||
@@ -1,23 +1,43 @@
|
||||
import { useContext } from 'react';
|
||||
import { useContext, useEffect, useRef } from 'react';
|
||||
|
||||
import { IonLifeCycleContext } from '../contexts/IonLifeCycleContext';
|
||||
import { IonLifeCycleContext, LifeCycleCallback } from '../contexts/IonLifeCycleContext';
|
||||
|
||||
export const useIonViewWillEnter = (callback: () => void) => {
|
||||
const value = useContext(IonLifeCycleContext);
|
||||
value.onIonViewWillEnter(callback);
|
||||
export const useIonViewWillEnter = (callback: LifeCycleCallback, deps: any[] = []) => {
|
||||
const context = useContext(IonLifeCycleContext);
|
||||
const id = useRef<number | undefined>();
|
||||
id.current = id.current || Math.floor(Math.random() * 1000000);
|
||||
useEffect(() => {
|
||||
callback.id = id.current!;
|
||||
context.onIonViewWillEnter(callback);
|
||||
}, deps);
|
||||
};
|
||||
|
||||
export const useIonViewDidEnter = (callback: () => void) => {
|
||||
const value = useContext(IonLifeCycleContext);
|
||||
value.onIonViewDidEnter(callback);
|
||||
export const useIonViewDidEnter = (callback: LifeCycleCallback, deps: any[] = []) => {
|
||||
const context = useContext(IonLifeCycleContext);
|
||||
const id = useRef<number | undefined>();
|
||||
id.current = id.current || Math.floor(Math.random() * 1000000);
|
||||
useEffect(() => {
|
||||
callback.id = id.current!;
|
||||
context.onIonViewDidEnter(callback);
|
||||
}, deps);
|
||||
};
|
||||
|
||||
export const useIonViewWillLeave = (callback: () => void) => {
|
||||
const value = useContext(IonLifeCycleContext);
|
||||
value.onIonViewWillLeave(callback);
|
||||
export const useIonViewWillLeave = (callback: LifeCycleCallback, deps: any[] = []) => {
|
||||
const context = useContext(IonLifeCycleContext);
|
||||
const id = useRef<number | undefined>();
|
||||
id.current = id.current || Math.floor(Math.random() * 1000000);
|
||||
useEffect(() => {
|
||||
callback.id = id.current!;
|
||||
context.onIonViewWillLeave(callback);
|
||||
}, deps);
|
||||
};
|
||||
|
||||
export const useIonViewDidLeave = (callback: () => void) => {
|
||||
const value = useContext(IonLifeCycleContext);
|
||||
value.onIonViewDidLeave(callback);
|
||||
export const useIonViewDidLeave = (callback: LifeCycleCallback, deps: any[] = []) => {
|
||||
const context = useContext(IonLifeCycleContext);
|
||||
const id = useRef<number | undefined>();
|
||||
id.current = id.current || Math.floor(Math.random() * 1000000);
|
||||
useEffect(() => {
|
||||
callback.id = id.current!;
|
||||
context.onIonViewDidLeave(callback);
|
||||
}, deps);
|
||||
};
|
||||
|
||||
@@ -14,12 +14,11 @@
|
||||
"trailing-comma": false,
|
||||
"no-null-keyword": false,
|
||||
"no-console": false,
|
||||
"no-unbound-method": true,
|
||||
"no-floating-promises": false,
|
||||
"no-invalid-template-strings": true,
|
||||
"ban-export-const-enum": true,
|
||||
"only-arrow-functions": true,
|
||||
"strict-boolean-conditions": [true, "allow-null-union", "allow-undefined-union", "allow-boolean-or-undefined", "allow-string"],
|
||||
"strict-boolean-conditions": [false],
|
||||
"jsx-key": false,
|
||||
"jsx-self-close": false,
|
||||
"jsx-curly-spacing": [true, "never"],
|
||||
@@ -27,6 +26,8 @@
|
||||
"jsx-no-bind": false,
|
||||
"jsx-no-lambda": false,
|
||||
"jsx-no-multiline-js": false,
|
||||
"jsx-wrap-multiline": false
|
||||
"jsx-wrap-multiline": false,
|
||||
"no-empty-interface": false,
|
||||
"no-unbound-method": false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user