mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-17 10:41:13 +08:00
chore(): React Build Scripts (#19501)
This commit is contained in:
@ -108,6 +108,58 @@ jobs:
|
|||||||
paths:
|
paths:
|
||||||
- "*"
|
- "*"
|
||||||
|
|
||||||
|
build-react:
|
||||||
|
<<: *defaults
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- attach_workspace:
|
||||||
|
at: /tmp/workspace
|
||||||
|
- run:
|
||||||
|
command: npm install
|
||||||
|
working_directory: /tmp/workspace/packages/react
|
||||||
|
- run:
|
||||||
|
command: sudo npm link
|
||||||
|
working_directory: /tmp/workspace/core
|
||||||
|
- run:
|
||||||
|
command: sudo npm link @ionic/core
|
||||||
|
working_directory: /tmp/workspace/packages/react
|
||||||
|
- run:
|
||||||
|
command: npm run build
|
||||||
|
working_directory: /tmp/workspace/packages/react
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: /tmp/workspace
|
||||||
|
paths:
|
||||||
|
- "*"
|
||||||
|
|
||||||
|
build-react-router:
|
||||||
|
<<: *defaults
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- attach_workspace:
|
||||||
|
at: /tmp/workspace
|
||||||
|
- run:
|
||||||
|
command: npm install
|
||||||
|
working_directory: /tmp/workspace/packages/react-router
|
||||||
|
- run:
|
||||||
|
command: sudo npm link
|
||||||
|
working_directory: /tmp/workspace/core
|
||||||
|
- run:
|
||||||
|
command: sudo npm link @ionic/core
|
||||||
|
working_directory: /tmp/workspace/packages/react-router
|
||||||
|
- run:
|
||||||
|
command: sudo npm link
|
||||||
|
working_directory: /tmp/workspace/packages/react
|
||||||
|
- run:
|
||||||
|
command: sudo npm link @ionic/react
|
||||||
|
working_directory: /tmp/workspace/packages/react-router
|
||||||
|
- run:
|
||||||
|
command: npm run build
|
||||||
|
working_directory: /tmp/workspace/packages/react-router
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: /tmp/workspace
|
||||||
|
paths:
|
||||||
|
- "*"
|
||||||
|
|
||||||
test-core-clean-build:
|
test-core-clean-build:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
steps:
|
steps:
|
||||||
@ -181,6 +233,64 @@ jobs:
|
|||||||
command: npm run lint
|
command: npm run lint
|
||||||
working_directory: /tmp/workspace/angular
|
working_directory: /tmp/workspace/angular
|
||||||
|
|
||||||
|
test-react-lint:
|
||||||
|
<<: *defaults
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- attach_workspace:
|
||||||
|
at: /tmp/workspace
|
||||||
|
- run:
|
||||||
|
command: npm run lint
|
||||||
|
working_directory: /tmp/workspace/packages/react
|
||||||
|
|
||||||
|
test-react-router-lint:
|
||||||
|
<<: *defaults
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- attach_workspace:
|
||||||
|
at: /tmp/workspace
|
||||||
|
- run:
|
||||||
|
command: npm run lint
|
||||||
|
working_directory: /tmp/workspace/packages/react-router
|
||||||
|
|
||||||
|
test-react-spec:
|
||||||
|
<<: *defaults
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- attach_workspace:
|
||||||
|
at: /tmp/workspace
|
||||||
|
- run:
|
||||||
|
command: sudo npm link
|
||||||
|
working_directory: /tmp/workspace/core
|
||||||
|
- run:
|
||||||
|
command: sudo npm link @ionic/core
|
||||||
|
working_directory: /tmp/workspace/packages/react
|
||||||
|
- run:
|
||||||
|
command: npm run test.spec
|
||||||
|
working_directory: /tmp/workspace/packages/react
|
||||||
|
|
||||||
|
test-react-router-spec:
|
||||||
|
<<: *defaults
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- attach_workspace:
|
||||||
|
at: /tmp/workspace
|
||||||
|
- run:
|
||||||
|
command: sudo npm link
|
||||||
|
working_directory: /tmp/workspace/core
|
||||||
|
- run:
|
||||||
|
command: sudo npm link @ionic/core
|
||||||
|
working_directory: /tmp/workspace/packages/react
|
||||||
|
- run:
|
||||||
|
command: sudo npm link
|
||||||
|
working_directory: /tmp/workspace/packages/react
|
||||||
|
- run:
|
||||||
|
command: sudo npm link @ionic/react
|
||||||
|
working_directory: /tmp/workspace/packages/react-router
|
||||||
|
- run:
|
||||||
|
command: npm run test.spec
|
||||||
|
working_directory: /tmp/workspace/packages/react-router
|
||||||
|
|
||||||
test-angular-e2e:
|
test-angular-e2e:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
steps:
|
steps:
|
||||||
@ -227,6 +337,18 @@ workflows:
|
|||||||
requires: [build-core]
|
requires: [build-core]
|
||||||
- build-angular-server:
|
- build-angular-server:
|
||||||
requires: [build-angular]
|
requires: [build-angular]
|
||||||
|
- build-react:
|
||||||
|
requires: [build-core]
|
||||||
|
- build-react-router:
|
||||||
|
requires: [build-core, build-react]
|
||||||
|
- test-react-lint:
|
||||||
|
requires: [build-react]
|
||||||
|
- test-react-router-lint:
|
||||||
|
requires: [build-react-router]
|
||||||
|
- test-react-spec:
|
||||||
|
requires: [build-react]
|
||||||
|
- test-react-router-spec:
|
||||||
|
requires: [build-react-router]
|
||||||
- test-angular-lint:
|
- test-angular-lint:
|
||||||
requires: [build-angular]
|
requires: [build-angular]
|
||||||
- test-angular-e2e:
|
- test-angular-e2e:
|
||||||
|
@ -11,6 +11,8 @@ const packages = [
|
|||||||
'core',
|
'core',
|
||||||
'docs',
|
'docs',
|
||||||
'angular',
|
'angular',
|
||||||
|
'packages/react',
|
||||||
|
'packages/react-router'
|
||||||
];
|
];
|
||||||
|
|
||||||
function readPkg(project) {
|
function readPkg(project) {
|
||||||
@ -36,7 +38,8 @@ function checkGit(tasks) {
|
|||||||
tasks.push(
|
tasks.push(
|
||||||
{
|
{
|
||||||
title: 'Check current branch',
|
title: 'Check current branch',
|
||||||
task: () => execa.stdout('git', ['symbolic-ref', '--short', 'HEAD']).then(branch => {
|
task: () =>
|
||||||
|
execa.stdout('git', ['symbolic-ref', '--short', 'HEAD']).then(branch => {
|
||||||
if (branch.indexOf('release') === -1 && branch.indexOf('hotfix') === -1) {
|
if (branch.indexOf('release') === -1 && branch.indexOf('hotfix') === -1) {
|
||||||
throw new Error(`Must be on a "release" or "hotfix" branch.`);
|
throw new Error(`Must be on a "release" or "hotfix" branch.`);
|
||||||
}
|
}
|
||||||
@ -44,7 +47,8 @@ function checkGit(tasks) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Check local working tree',
|
title: 'Check local working tree',
|
||||||
task: () => execa.stdout('git', ['status', '--porcelain']).then(status => {
|
task: () =>
|
||||||
|
execa.stdout('git', ['status', '--porcelain']).then(status => {
|
||||||
if (status !== '') {
|
if (status !== '') {
|
||||||
throw new Error(`Unclean working tree. Commit or stash changes first.`);
|
throw new Error(`Unclean working tree. Commit or stash changes first.`);
|
||||||
}
|
}
|
||||||
@ -52,7 +56,8 @@ function checkGit(tasks) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Check remote history',
|
title: 'Check remote history',
|
||||||
task: () => execa.stdout('git', ['rev-list', '--count', '--left-only', '@{u}...HEAD']).then(result => {
|
task: () =>
|
||||||
|
execa.stdout('git', ['rev-list', '--count', '--left-only', '@{u}...HEAD']).then(result => {
|
||||||
if (result !== '0') {
|
if (result !== '0') {
|
||||||
throw new Error(`Remote history differs. Please pull changes.`);
|
throw new Error(`Remote history differs. Please pull changes.`);
|
||||||
}
|
}
|
||||||
@ -61,8 +66,19 @@ function checkGit(tasks) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isValidVersion = input => Boolean(semver.valid(input));
|
function checkTestDist(tasks) {
|
||||||
|
tasks.push({
|
||||||
|
title: 'Check dist folders for required files',
|
||||||
|
task: () =>
|
||||||
|
execa.stdout('node', ['.scripts/test-dist.js']).then(status => {
|
||||||
|
if (status.indexOf('✅ test.dist') === -1) {
|
||||||
|
throw new Error(`Test Dist did not find some required files`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const isValidVersion = input => Boolean(semver.valid(input));
|
||||||
|
|
||||||
function preparePackage(tasks, package, version, install) {
|
function preparePackage(tasks, package, version, install) {
|
||||||
const projectRoot = projectPath(package);
|
const projectRoot = projectPath(package);
|
||||||
@ -74,7 +90,9 @@ function preparePackage(tasks, package, version, install) {
|
|||||||
title: `${pkg.name}: validate new version`,
|
title: `${pkg.name}: validate new version`,
|
||||||
task: () => {
|
task: () => {
|
||||||
if (!isVersionGreater(pkg.version, version)) {
|
if (!isVersionGreater(pkg.version, version)) {
|
||||||
throw new Error(`New version \`${version}\` should be higher than current version \`${pkg.version}\``);
|
throw new Error(
|
||||||
|
`New version \`${version}\` should be higher than current version \`${pkg.version}\``
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -82,7 +100,7 @@ function preparePackage(tasks, package, version, install) {
|
|||||||
projectTasks.push({
|
projectTasks.push({
|
||||||
title: `${pkg.name}: install npm dependencies`,
|
title: `${pkg.name}: install npm dependencies`,
|
||||||
task: async () => {
|
task: async () => {
|
||||||
await fs.remove(path.join(projectRoot, 'node_modules'))
|
await fs.remove(path.join(projectRoot, 'node_modules'));
|
||||||
await execa('npm', ['i'], { cwd: projectRoot });
|
await execa('npm', ['i'], { cwd: projectRoot });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -95,6 +113,13 @@ function preparePackage(tasks, package, version, install) {
|
|||||||
title: `${pkg.name}: npm link @ionic/core`,
|
title: `${pkg.name}: npm link @ionic/core`,
|
||||||
task: () => execa('npm', ['link', '@ionic/core'], { cwd: projectRoot })
|
task: () => execa('npm', ['link', '@ionic/core'], { cwd: projectRoot })
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (package === 'packages/react-router') {
|
||||||
|
projectTasks.push({
|
||||||
|
title: `${pkg.name}: npm link @ionic/react`,
|
||||||
|
task: () => execa('npm', ['link', '@ionic/react'], { cwd: projectRoot })
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version) {
|
if (version) {
|
||||||
@ -105,7 +130,7 @@ function preparePackage(tasks, package, version, install) {
|
|||||||
projectTasks.push({
|
projectTasks.push({
|
||||||
title: `${pkg.name}: update ionic/core dep to ${version}`,
|
title: `${pkg.name}: update ionic/core dep to ${version}`,
|
||||||
task: () => {
|
task: () => {
|
||||||
updateDependency(pkg, "@ionic/core", version);
|
updateDependency(pkg, '@ionic/core', version);
|
||||||
writePkg(package, pkg);
|
writePkg(package, pkg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -134,7 +159,6 @@ function preparePackage(tasks, package, version, install) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function prepareDevPackage(tasks, package, version) {
|
function prepareDevPackage(tasks, package, version) {
|
||||||
const projectRoot = projectPath(package);
|
const projectRoot = projectPath(package);
|
||||||
const pkg = readPkg(package);
|
const pkg = readPkg(package);
|
||||||
@ -152,7 +176,7 @@ function prepareDevPackage(tasks, package, version) {
|
|||||||
projectTasks.push({
|
projectTasks.push({
|
||||||
title: `${pkg.name}: update ionic/core dep to ${version}`,
|
title: `${pkg.name}: update ionic/core dep to ${version}`,
|
||||||
task: () => {
|
task: () => {
|
||||||
updateDependency(pkg, "@ionic/core", version);
|
updateDependency(pkg, '@ionic/core', version);
|
||||||
writePkg(package, pkg);
|
writePkg(package, pkg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -181,8 +205,7 @@ function updatePackageVersions(tasks, packages, version) {
|
|||||||
packages.forEach(package => {
|
packages.forEach(package => {
|
||||||
updatePackageVersion(tasks, package, version);
|
updatePackageVersion(tasks, package, version);
|
||||||
|
|
||||||
tasks.push(
|
tasks.push({
|
||||||
{
|
|
||||||
title: `${package} update @ionic/core dependency, if present ${tc.dim(`(${version})`)}`,
|
title: `${package} update @ionic/core dependency, if present ${tc.dim(`(${version})`)}`,
|
||||||
task: async () => {
|
task: async () => {
|
||||||
if (package !== 'core') {
|
if (package !== 'core') {
|
||||||
@ -190,24 +213,30 @@ function updatePackageVersions(tasks, packages, version) {
|
|||||||
updateDependency(pkg, '@ionic/core', version);
|
updateDependency(pkg, '@ionic/core', version);
|
||||||
writePkg(package, pkg);
|
writePkg(package, pkg);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
}
|
}
|
||||||
)
|
});
|
||||||
|
if (package === 'packages/react-router') {
|
||||||
|
tasks.push({
|
||||||
|
title: `${package} update @ionic/react dependency, if present ${tc.dim(`(${version})`)}`,
|
||||||
|
task: async () => {
|
||||||
|
const pkg = readPkg(package);
|
||||||
|
updateDependency(pkg, '@ionic/react', version);
|
||||||
|
writePkg(package, pkg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function updatePackageVersion(tasks, package, version) {
|
function updatePackageVersion(tasks, package, version) {
|
||||||
const projectRoot = projectPath(package);
|
const projectRoot = projectPath(package);
|
||||||
|
|
||||||
tasks.push(
|
tasks.push({
|
||||||
{
|
|
||||||
title: `${package}: update package.json ${tc.dim(`(${version})`)}`,
|
title: `${package}: update package.json ${tc.dim(`(${version})`)}`,
|
||||||
task: async () => {
|
task: async () => {
|
||||||
await execa('npm', ['version', version], { cwd: projectRoot });
|
await execa('npm', ['version', version], { cwd: projectRoot });
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function publishPackages(tasks, packages, version, tag = 'latest') {
|
function publishPackages(tasks, packages, version, tag = 'latest') {
|
||||||
@ -237,7 +266,7 @@ function publishPackages(tasks, packages, version, tag = 'latest') {
|
|||||||
title: `${package}: publish to ${tag} tag`,
|
title: `${package}: publish to ${tag} tag`,
|
||||||
task: async () => {
|
task: async () => {
|
||||||
await execa('npm', ['publish', '--tag', tag], { cwd: projectRoot });
|
await execa('npm', ['publish', '--tag', tag], { cwd: projectRoot });
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -261,11 +290,12 @@ function isVersionGreater(oldVersion, newVersion) {
|
|||||||
function copyCDNLoader(tasks, version) {
|
function copyCDNLoader(tasks, version) {
|
||||||
tasks.push({
|
tasks.push({
|
||||||
title: `Copy CDN loader`,
|
title: `Copy CDN loader`,
|
||||||
task: () => execa('node', ['copy-cdn-loader.js', version], { cwd: path.join(rootDir, 'core', 'scripts') }),
|
task: () => execa('node', ['copy-cdn-loader.js', version], { cwd: path.join(rootDir, 'core', 'scripts') })
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
checkTestDist,
|
||||||
checkGit,
|
checkGit,
|
||||||
isValidVersion,
|
isValidVersion,
|
||||||
isVersionGreater,
|
isVersionGreater,
|
||||||
@ -281,5 +311,5 @@ module.exports = {
|
|||||||
updateDependency,
|
updateDependency,
|
||||||
updatePackageVersion,
|
updatePackageVersion,
|
||||||
updatePackageVersions,
|
updatePackageVersions,
|
||||||
writePkg,
|
writePkg
|
||||||
};
|
};
|
||||||
|
@ -107,13 +107,14 @@ async function preparePackages(packages, version, install) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// add update package.json of each project
|
// add update package.json of each project
|
||||||
packages.forEach(package => {
|
common.updatePackageVersions(tasks, packages, version);
|
||||||
common.updatePackageVersion(tasks, package, version);
|
|
||||||
});
|
|
||||||
|
|
||||||
// generate changelog
|
// generate changelog
|
||||||
generateChangeLog(tasks);
|
generateChangeLog(tasks);
|
||||||
|
|
||||||
|
// check dist folders
|
||||||
|
common.checkTestDist(tasks);
|
||||||
|
|
||||||
// update core readme with version number
|
// update core readme with version number
|
||||||
updateCoreReadme(tasks, version);
|
updateCoreReadme(tasks, version);
|
||||||
common.copyCDNLoader(tasks, version);
|
common.copyCDNLoader(tasks, version);
|
||||||
|
82
.scripts/test-dist.js
Normal file
82
.scripts/test-dist.js
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Test dist build:
|
||||||
|
// Double-triple check all the packages
|
||||||
|
// and files are good to go before publishing
|
||||||
|
[
|
||||||
|
// core
|
||||||
|
{
|
||||||
|
files: ['../core/dist/index.js', '../core/dist/ionic/index.esm.js']
|
||||||
|
},
|
||||||
|
// angular
|
||||||
|
{
|
||||||
|
files: ['../angular/dist/fesm5.cjs.js']
|
||||||
|
},
|
||||||
|
// react
|
||||||
|
{
|
||||||
|
files: ['../packages/react/dist/index.js']
|
||||||
|
},
|
||||||
|
// react-router
|
||||||
|
{
|
||||||
|
files: ['../packages/react-router/dist/index.js']
|
||||||
|
}
|
||||||
|
].forEach(testPackage);
|
||||||
|
|
||||||
|
function testPackage(testPkg) {
|
||||||
|
if (testPkg.packageJson) {
|
||||||
|
const pkgDir = path.dirname(testPkg.packageJson);
|
||||||
|
const pkgJson = require(testPkg.packageJson);
|
||||||
|
|
||||||
|
if (!pkgJson.name) {
|
||||||
|
throw new Error('missing package.json name: ' + testPkg.packageJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pkgJson.main) {
|
||||||
|
throw new Error('missing package.json main: ' + testPkg.packageJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pkgPath = path.join(pkgDir, pkgJson.main);
|
||||||
|
const pkgImport = require(pkgPath);
|
||||||
|
|
||||||
|
if (testPkg.files) {
|
||||||
|
if (!Array.isArray(pkgJson.files)) {
|
||||||
|
throw new Error(testPkg.packageJson + ' missing "files" property');
|
||||||
|
}
|
||||||
|
testPkg.files.forEach(testPkgFile => {
|
||||||
|
if (!pkgJson.files.includes(testPkgFile)) {
|
||||||
|
throw new Error(testPkg.packageJson + ' missing file ' + testPkgFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
const filePath = path.join(__dirname, pkgDir, testPkgFile);
|
||||||
|
fs.accessSync(filePath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pkgJson.module) {
|
||||||
|
const moduleIndex = path.join(__dirname, pkgDir, pkgJson.module);
|
||||||
|
fs.accessSync(moduleIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pkgJson.types) {
|
||||||
|
const pkgTypes = path.join(__dirname, pkgDir, pkgJson.types);
|
||||||
|
fs.accessSync(pkgTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testPkg.exports) {
|
||||||
|
testPkg.exports.forEach(exportName => {
|
||||||
|
const m = pkgImport[exportName];
|
||||||
|
if (!m) {
|
||||||
|
throw new Error('export "' + exportName + '" not found in: ' + testPkg.packageJson);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (testPkg.files) {
|
||||||
|
testPkg.files.forEach(file => {
|
||||||
|
const filePath = path.join(__dirname, file);
|
||||||
|
fs.statSync(filePath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`✅ test.dist`);
|
@ -1,5 +1,3 @@
|
|||||||
global.crypto = require('@trust/webcrypto');
|
|
||||||
|
|
||||||
window.matchMedia = window.matchMedia || function() {
|
window.matchMedia = window.matchMedia || function() {
|
||||||
return {
|
return {
|
||||||
matches : false,
|
matches : false,
|
||||||
|
@ -24,8 +24,10 @@
|
|||||||
"clean": "rm -rf dist dist-transpiled",
|
"clean": "rm -rf dist dist-transpiled",
|
||||||
"compile": "npm run tsc && rollup -c",
|
"compile": "npm run tsc && rollup -c",
|
||||||
"release": "np --any-branch --no-cleanup",
|
"release": "np --any-branch --no-cleanup",
|
||||||
|
"lint": "tslint --project .",
|
||||||
|
"lint.fix": "tslint --project . --fix",
|
||||||
"tsc": "tsc -p .",
|
"tsc": "tsc -p .",
|
||||||
"test": "jest"
|
"test.spec": "jest --ci"
|
||||||
},
|
},
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"module": "dist/index.esm.js",
|
"module": "dist/index.esm.js",
|
||||||
@ -34,7 +36,10 @@
|
|||||||
"dist/"
|
"dist/"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "*"
|
"tslib": "*",
|
||||||
|
"tslint": "^5.20.0",
|
||||||
|
"tslint-ionic-rules": "0.0.21",
|
||||||
|
"tslint-react": "^4.1.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ionic/core": "^4.10.0",
|
"@ionic/core": "^4.10.0",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { match, RouteProps } from 'react-router-dom';
|
import { RouteProps, match } from 'react-router-dom';
|
||||||
|
|
||||||
export interface IonRouteData {
|
export interface IonRouteData {
|
||||||
match: match<{ tab: string }> | null;
|
match: match<{ tab: string }> | null;
|
||||||
|
@ -3,9 +3,11 @@ import { NavContext, NavContextState } from '@ionic/react';
|
|||||||
import { Location as HistoryLocation, UnregisterCallback } from 'history';
|
import { Location as HistoryLocation, UnregisterCallback } from 'history';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { RouteComponentProps } from 'react-router-dom';
|
import { RouteComponentProps } from 'react-router-dom';
|
||||||
import { StackManager } from './StackManager';
|
|
||||||
import { generateId } from '../utils';
|
import { generateId } from '../utils';
|
||||||
import { LocationHistory } from '../utils/LocationHistory'
|
import { LocationHistory } from '../utils/LocationHistory';
|
||||||
|
|
||||||
|
import { StackManager } from './StackManager';
|
||||||
import { ViewItem } from './ViewItem';
|
import { ViewItem } from './ViewItem';
|
||||||
import { ViewStack } from './ViewStacks';
|
import { ViewStack } from './ViewStacks';
|
||||||
|
|
||||||
@ -13,12 +15,11 @@ interface NavManagerProps extends RouteComponentProps {
|
|||||||
findViewInfoByLocation: (location: HistoryLocation) => { view?: ViewItem, viewStack?: ViewStack };
|
findViewInfoByLocation: (location: HistoryLocation) => { view?: ViewItem, viewStack?: ViewStack };
|
||||||
findViewInfoById: (id: string) => { view?: ViewItem, viewStack?: ViewStack };
|
findViewInfoById: (id: string) => { view?: ViewItem, viewStack?: ViewStack };
|
||||||
getActiveIonPage: () => { view?: ViewItem, viewStack?: ViewStack };
|
getActiveIonPage: () => { view?: ViewItem, viewStack?: ViewStack };
|
||||||
};
|
}
|
||||||
interface NavManagerState extends NavContextState {};
|
|
||||||
|
|
||||||
export class NavManager extends React.Component<NavManagerProps, NavManagerState> {
|
export class NavManager extends React.Component<NavManagerProps, NavContextState> {
|
||||||
|
|
||||||
listenUnregisterCallback: UnregisterCallback;
|
listenUnregisterCallback: UnregisterCallback | undefined;
|
||||||
locationHistory: LocationHistory = new LocationHistory();
|
locationHistory: LocationHistory = new LocationHistory();
|
||||||
|
|
||||||
constructor(props: NavManagerProps) {
|
constructor(props: NavManagerProps) {
|
||||||
@ -32,13 +33,13 @@ export class NavManager extends React.Component<NavManagerProps, NavManagerState
|
|||||||
getStackManager: this.getStackManager.bind(this),
|
getStackManager: this.getStackManager.bind(this),
|
||||||
getPageManager: this.getPageManager.bind(this),
|
getPageManager: this.getPageManager.bind(this),
|
||||||
currentPath: this.props.location.pathname,
|
currentPath: this.props.location.pathname,
|
||||||
registerIonPage: () => {} //overridden in View for each IonPage
|
registerIonPage: () => { return; } // overridden in View for each IonPage
|
||||||
}
|
};
|
||||||
|
|
||||||
this.listenUnregisterCallback = this.props.history.listen((location: HistoryLocation) => {
|
this.listenUnregisterCallback = this.props.history.listen((location: HistoryLocation) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
currentPath: location.pathname
|
currentPath: location.pathname
|
||||||
})
|
});
|
||||||
this.locationHistory.add(location);
|
this.locationHistory.add(location);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -69,10 +70,14 @@ export class NavManager extends React.Component<NavManagerProps, NavManagerState
|
|||||||
this.props.history.replace(enteringView.routeData.match.url, { direction: 'back' });
|
this.props.history.replace(enteringView.routeData.match.url, { direction: 'back' });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
defaultHref && this.props.history.replace(defaultHref, { direction: 'back' });
|
if (defaultHref) {
|
||||||
|
this.props.history.replace(defaultHref, { direction: 'back' });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
defaultHref && this.props.history.replace(defaultHref, { direction: 'back' });
|
if (defaultHref) {
|
||||||
|
this.props.history.replace(defaultHref, { direction: 'back' });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { ReactNode } from 'react';
|
|
||||||
import { NavDirection } from '@ionic/core';
|
import { NavDirection } from '@ionic/core';
|
||||||
|
import React, { ReactNode } from 'react';
|
||||||
|
|
||||||
import { ViewStacks } from './ViewStacks';
|
import { ViewStacks } from './ViewStacks';
|
||||||
|
|
||||||
export interface RouteManagerContextState {
|
export interface RouteManagerContextState {
|
||||||
@ -15,11 +16,11 @@ export const RouteManagerContext = /*@__PURE__*/React.createContext<RouteManager
|
|||||||
viewStacks: new ViewStacks(),
|
viewStacks: new ViewStacks(),
|
||||||
syncView: () => { navContextNotFoundError(); },
|
syncView: () => { navContextNotFoundError(); },
|
||||||
hideView: () => { navContextNotFoundError(); },
|
hideView: () => { navContextNotFoundError(); },
|
||||||
setupIonRouter: () => { return Promise.reject(navContextNotFoundError()) },
|
setupIonRouter: () => Promise.reject(navContextNotFoundError()),
|
||||||
removeViewStack: () => { navContextNotFoundError(); },
|
removeViewStack: () => { navContextNotFoundError(); },
|
||||||
transitionView: () => { navContextNotFoundError(); }
|
transitionView: () => { navContextNotFoundError(); }
|
||||||
});
|
});
|
||||||
|
|
||||||
function navContextNotFoundError() {
|
function navContextNotFoundError() {
|
||||||
console.error('IonReactRouter not found, did you add it to the app?')
|
console.error('IonReactRouter not found, did you add it to the app?');
|
||||||
}
|
}
|
||||||
|
@ -2,27 +2,26 @@ import { NavDirection } from '@ionic/core';
|
|||||||
import { RouterDirection } from '@ionic/react';
|
import { RouterDirection } from '@ionic/react';
|
||||||
import { Action as HistoryAction, Location as HistoryLocation, UnregisterCallback } from 'history';
|
import { Action as HistoryAction, Location as HistoryLocation, UnregisterCallback } from 'history';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { BrowserRouter, BrowserRouterProps, matchPath, RouteComponentProps, withRouter } from 'react-router-dom';
|
import { BrowserRouter, BrowserRouterProps, RouteComponentProps, matchPath, withRouter } from 'react-router-dom';
|
||||||
|
|
||||||
import { generateId } from '../utils';
|
import { generateId } from '../utils';
|
||||||
|
|
||||||
import { IonRouteData } from './IonRouteData';
|
import { IonRouteData } from './IonRouteData';
|
||||||
import { NavManager } from './NavManager';
|
import { NavManager } from './NavManager';
|
||||||
import { RouteManagerContext, RouteManagerContextState } from './RouteManagerContext';
|
import { RouteManagerContext, RouteManagerContextState } from './RouteManagerContext';
|
||||||
import { ViewItem } from './ViewItem';
|
import { ViewItem } from './ViewItem';
|
||||||
import { ViewStacks, ViewStack } from './ViewStacks';
|
import { ViewStack, ViewStacks } from './ViewStacks';
|
||||||
|
|
||||||
|
|
||||||
interface RouteManagerProps extends RouteComponentProps { }
|
|
||||||
|
|
||||||
interface RouteManagerState extends RouteManagerContextState {
|
interface RouteManagerState extends RouteManagerContextState {
|
||||||
location?: HistoryLocation,
|
location?: HistoryLocation;
|
||||||
action?: HistoryAction
|
action?: HistoryAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
class RouteManager extends React.Component<RouteManagerProps, RouteManagerState> {
|
class RouteManager extends React.Component<RouteComponentProps, RouteManagerState> {
|
||||||
listenUnregisterCallback: UnregisterCallback | undefined;
|
listenUnregisterCallback: UnregisterCallback | undefined;
|
||||||
activeIonPageId?: string;
|
activeIonPageId?: string;
|
||||||
|
|
||||||
constructor(props: RouteManagerProps) {
|
constructor(props: RouteComponentProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.listenUnregisterCallback = this.props.history.listen(this.historyChange.bind(this));
|
this.listenUnregisterCallback = this.props.history.listen(this.historyChange.bind(this));
|
||||||
this.state = {
|
this.state = {
|
||||||
@ -35,7 +34,7 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(_prevProps: RouteManagerProps, prevState: RouteManagerState) {
|
componentDidUpdate(_prevProps: RouteComponentProps, prevState: RouteManagerState) {
|
||||||
// Trigger a page change if the location or action is different
|
// 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) {
|
if (this.state.location && prevState.location !== this.state.location || prevState.action !== this.state.action) {
|
||||||
this.setActiveView(this.state.location!, this.state.action!);
|
this.setActiveView(this.state.location!, this.state.action!);
|
||||||
@ -61,7 +60,7 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
|
|||||||
this.setState({
|
this.setState({
|
||||||
location,
|
location,
|
||||||
action
|
action
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setActiveView(location: HistoryLocation, action: HistoryAction) {
|
setActiveView(location: HistoryLocation, action: HistoryAction) {
|
||||||
@ -77,8 +76,6 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
|
|||||||
}
|
}
|
||||||
leavingView = viewStacks.findViewInfoById(this.activeIonPageId).view;
|
leavingView = viewStacks.findViewInfoById(this.activeIonPageId).view;
|
||||||
|
|
||||||
if (enteringView) {
|
|
||||||
|
|
||||||
if (enteringView.isIonRoute) {
|
if (enteringView.isIonRoute) {
|
||||||
enteringView.show = true;
|
enteringView.show = true;
|
||||||
enteringView.mount = true;
|
enteringView.mount = true;
|
||||||
@ -107,7 +104,6 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
|
|||||||
enteringView.mount = true;
|
enteringView.mount = true;
|
||||||
enteringView.routeData.match = match!;
|
enteringView.routeData.match = match!;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (leavingView) {
|
if (leavingView) {
|
||||||
@ -120,7 +116,7 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
|
|||||||
this.setState({
|
this.setState({
|
||||||
viewStacks
|
viewStacks
|
||||||
}, () => {
|
}, () => {
|
||||||
const { view: enteringView, viewStack } = this.state.viewStacks.findViewInfoById(this.activeIonPageId)
|
const { view: enteringView, viewStack } = this.state.viewStacks.findViewInfoById(this.activeIonPageId);
|
||||||
if (enteringView && viewStack) {
|
if (enteringView && viewStack) {
|
||||||
const enteringEl = enteringView.ionPageElement ? enteringView.ionPageElement : undefined;
|
const enteringEl = enteringView.ionPageElement ? enteringView.ionPageElement : undefined;
|
||||||
const leavingEl = leavingView && leavingView.ionPageElement ? leavingView.ionPageElement : undefined;
|
const leavingEl = leavingView && leavingView.ionPageElement ? leavingView.ionPageElement : undefined;
|
||||||
@ -132,7 +128,7 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
|
|||||||
enteringEl!,
|
enteringEl!,
|
||||||
leavingEl!,
|
leavingEl!,
|
||||||
viewStack.routerOutlet,
|
viewStack.routerOutlet,
|
||||||
navDirection)
|
navDirection);
|
||||||
} else if (leavingEl) {
|
} else if (leavingEl) {
|
||||||
leavingEl.classList.add('ion-page-hidden');
|
leavingEl.classList.add('ion-page-hidden');
|
||||||
leavingEl.setAttribute('aria-hidden', 'true');
|
leavingEl.setAttribute('aria-hidden', 'true');
|
||||||
@ -142,7 +138,9 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.listenUnregisterCallback && this.listenUnregisterCallback();
|
if (this.listenUnregisterCallback) {
|
||||||
|
this.listenUnregisterCallback();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async setupIonRouter(id: string, children: any, routerOutlet: HTMLIonRouterOutletElement) {
|
async setupIonRouter(id: string, children: any, routerOutlet: HTMLIonRouterOutletElement) {
|
||||||
@ -172,12 +170,12 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
|
|||||||
match,
|
match,
|
||||||
childProps: child.props
|
childProps: child.props
|
||||||
},
|
},
|
||||||
route: route,
|
route,
|
||||||
mount: true,
|
mount: true,
|
||||||
show: !!match,
|
show: !!match,
|
||||||
isIonRoute: false
|
isIonRoute: false
|
||||||
};
|
};
|
||||||
if (!!match && view.isIonRoute) {
|
if (match && view.isIonRoute) {
|
||||||
activeId = viewId;
|
activeId = viewId;
|
||||||
}
|
}
|
||||||
return view;
|
return view;
|
||||||
@ -186,9 +184,9 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
|
|||||||
|
|
||||||
async registerViewStack(stack: string, activeId: string | undefined, stackItems: ViewItem[], routerOutlet: HTMLIonRouterOutletElement, _location: HistoryLocation) {
|
async registerViewStack(stack: string, activeId: string | undefined, stackItems: ViewItem[], routerOutlet: HTMLIonRouterOutletElement, _location: HistoryLocation) {
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise(resolve => {
|
||||||
this.setState((prevState) => {
|
this.setState(prevState => {
|
||||||
const prevViewStacks = Object.assign(new ViewStacks, prevState.viewStacks);
|
const prevViewStacks = Object.assign(new ViewStacks(), prevState.viewStacks);
|
||||||
const newStack: ViewStack = {
|
const newStack: ViewStack = {
|
||||||
id: stack,
|
id: stack,
|
||||||
views: stackItems,
|
views: stackItems,
|
||||||
@ -205,7 +203,7 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
|
|||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
removeViewStack(stack: string) {
|
removeViewStack(stack: string) {
|
||||||
const viewStacks = Object.assign(new ViewStacks(), this.state.viewStacks);
|
const viewStacks = Object.assign(new ViewStacks(), this.state.viewStacks);
|
||||||
@ -216,7 +214,7 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
|
|||||||
}
|
}
|
||||||
|
|
||||||
syncView(page: HTMLElement, viewId: string) {
|
syncView(page: HTMLElement, viewId: string) {
|
||||||
this.setState((state) => {
|
this.setState(state => {
|
||||||
|
|
||||||
const viewStacks = Object.assign(new ViewStacks(), state.viewStacks);
|
const viewStacks = Object.assign(new ViewStacks(), state.viewStacks);
|
||||||
const { view } = viewStacks.findViewInfoById(viewId);
|
const { view } = viewStacks.findViewInfoById(viewId);
|
||||||
@ -226,14 +224,14 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
viewStacks
|
viewStacks
|
||||||
}
|
};
|
||||||
|
|
||||||
}, () => {
|
}, () => {
|
||||||
this.setActiveView(this.state.location || this.props.location, this.state.action!);
|
this.setActiveView(this.state.location || this.props.location, this.state.action!);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
transitionView(enteringEl: HTMLElement, leavingEl: HTMLElement, ionRouterOutlet: HTMLIonRouterOutletElement, direction?: NavDirection) {
|
transitionView(enteringEl: HTMLElement, leavingEl: HTMLElement, ionRouterOutlet?: HTMLIonRouterOutletElement, direction?: NavDirection) {
|
||||||
/**
|
/**
|
||||||
* Super hacky workaround to make sure ionRouterOutlet is available
|
* Super hacky workaround to make sure ionRouterOutlet is available
|
||||||
* since transitionView might be called before IonRouterOutlet is fully mounted
|
* since transitionView might be called before IonRouterOutlet is fully mounted
|
||||||
@ -256,7 +254,7 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
|
|||||||
await ionRouterOuter.commit(enteringEl, leavingEl, {
|
await ionRouterOuter.commit(enteringEl, leavingEl, {
|
||||||
deepWait: true,
|
deepWait: true,
|
||||||
duration: direction === undefined ? 0 : undefined,
|
duration: direction === undefined ? 0 : undefined,
|
||||||
direction: direction,
|
direction,
|
||||||
showGoBack: direction === 'forward',
|
showGoBack: direction === 'forward',
|
||||||
progressAnimation: false
|
progressAnimation: false
|
||||||
});
|
});
|
||||||
@ -271,7 +269,8 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<RouteManagerContext.Provider value={this.state}>
|
<RouteManagerContext.Provider value={this.state}>
|
||||||
<NavManager {...this.props}
|
<NavManager
|
||||||
|
{...this.props}
|
||||||
findViewInfoById={(id: string) => this.state.viewStacks.findViewInfoById(id)}
|
findViewInfoById={(id: string) => this.state.viewStacks.findViewInfoById(id)}
|
||||||
findViewInfoByLocation={(location: HistoryLocation) => this.state.viewStacks.findViewInfoByLocation(location)}
|
findViewInfoByLocation={(location: HistoryLocation) => this.state.viewStacks.findViewInfoByLocation(location)}
|
||||||
getActiveIonPage={() => this.state.viewStacks.findViewInfoById(this.activeIonPageId)}
|
getActiveIonPage={() => this.state.viewStacks.findViewInfoById(this.activeIonPageId)}
|
||||||
@ -281,7 +280,7 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
|
|||||||
</RouteManagerContext.Provider>
|
</RouteManagerContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const RouteManagerWithRouter = withRouter(RouteManager);
|
const RouteManagerWithRouter = withRouter(RouteManager);
|
||||||
RouteManagerWithRouter.displayName = 'RouteManager';
|
RouteManagerWithRouter.displayName = 'RouteManager';
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { generateId, isDevMode } from '../utils';
|
import { generateId, isDevMode } from '../utils';
|
||||||
import { View } from './View';
|
|
||||||
import { ViewTransitionManager } from './ViewTransitionManager';
|
|
||||||
import { RouteManagerContext } from './RouteManagerContext';
|
import { RouteManagerContext } from './RouteManagerContext';
|
||||||
|
import { View } from './View';
|
||||||
import { ViewItem } from './ViewItem';
|
import { ViewItem } from './ViewItem';
|
||||||
|
import { ViewTransitionManager } from './ViewTransitionManager';
|
||||||
|
|
||||||
type StackManagerProps = {
|
interface StackManagerProps {
|
||||||
id?: string;
|
id?: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
type StackManagerState = {}
|
export class StackManager extends React.Component<StackManagerProps, {}> {
|
||||||
|
|
||||||
export class StackManager extends React.Component<StackManagerProps, StackManagerState> {
|
|
||||||
routerOutletEl: React.RefObject<HTMLIonRouterOutletElement> = React.createRef();
|
routerOutletEl: React.RefObject<HTMLIonRouterOutletElement> = React.createRef();
|
||||||
context!: React.ContextType<typeof RouteManagerContext>;
|
context!: React.ContextType<typeof RouteManagerContext>;
|
||||||
id: string;
|
id: string;
|
||||||
@ -52,7 +52,7 @@ export class StackManager extends React.Component<StackManagerProps, StackManage
|
|||||||
const views = (viewStack || { views: [] }).views.filter(x => x.show);
|
const views = (viewStack || { views: [] }).views.filter(x => x.show);
|
||||||
const ionRouterOutlet = React.Children.only(this.props.children) as React.ReactElement;
|
const ionRouterOutlet = React.Children.only(this.props.children) as React.ReactElement;
|
||||||
|
|
||||||
const childElements = views.map((view) => {
|
const childElements = views.map(view => {
|
||||||
return (
|
return (
|
||||||
<ViewTransitionManager
|
<ViewTransitionManager
|
||||||
id={view.id}
|
id={view.id}
|
||||||
@ -72,15 +72,14 @@ export class StackManager extends React.Component<StackManagerProps, StackManage
|
|||||||
|
|
||||||
const elementProps: any = {
|
const elementProps: any = {
|
||||||
ref: this.routerOutletEl
|
ref: this.routerOutletEl
|
||||||
}
|
};
|
||||||
|
|
||||||
if (isDevMode()) {
|
if (isDevMode()) {
|
||||||
elementProps['data-stack-id'] = this.id
|
elementProps['data-stack-id'] = this.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
const routerOutletChild = React.cloneElement(ionRouterOutlet, elementProps, childElements);
|
const routerOutletChild = React.cloneElement(ionRouterOutlet, elementProps, childElements);
|
||||||
|
|
||||||
|
|
||||||
return routerOutletChild;
|
return routerOutletChild;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import React from 'react';
|
|
||||||
import { IonLifeCycleContext, NavContext } from '@ionic/react';
|
import { IonLifeCycleContext, NavContext } from '@ionic/react';
|
||||||
import { ViewItem } from './ViewItem';
|
import React from 'react';
|
||||||
import { Route, Redirect } from 'react-router-dom';
|
import { Redirect, Route } from 'react-router-dom';
|
||||||
|
|
||||||
import { isDevMode } from '../utils';
|
import { isDevMode } from '../utils';
|
||||||
|
|
||||||
|
import { ViewItem } from './ViewItem';
|
||||||
|
|
||||||
interface ViewProps extends React.HTMLAttributes<HTMLElement> {
|
interface ViewProps extends React.HTMLAttributes<HTMLElement> {
|
||||||
onViewSync: (page: HTMLElement, viewId: string) => void;
|
onViewSync: (page: HTMLElement, viewId: string) => void;
|
||||||
onHideView: (viewId: string) => void;
|
onHideView: (viewId: string) => void;
|
||||||
view: ViewItem;
|
view: ViewItem;
|
||||||
};
|
}
|
||||||
|
|
||||||
interface StackViewState { }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The View component helps manage the IonPage's lifecycle and registration
|
* The View component helps manage the IonPage's lifecycle and registration
|
||||||
*/
|
*/
|
||||||
export class View extends React.Component<ViewProps, StackViewState> {
|
export class View extends React.Component<ViewProps, {}> {
|
||||||
context!: React.ContextType<typeof IonLifeCycleContext>;
|
context!: React.ContextType<typeof IonLifeCycleContext>;
|
||||||
ionPage?: HTMLElement;
|
ionPage?: HTMLElement;
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ export class View extends React.Component<ViewProps, StackViewState> {
|
|||||||
const newProvider = {
|
const newProvider = {
|
||||||
...value,
|
...value,
|
||||||
registerIonPage: this.registerIonPage.bind(this)
|
registerIonPage: this.registerIonPage.bind(this)
|
||||||
}
|
};
|
||||||
return (
|
return (
|
||||||
<NavContext.Provider value={newProvider}>
|
<NavContext.Provider value={newProvider}>
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { deprecationWarning } from '../utils';
|
import { deprecationWarning } from '../utils';
|
||||||
|
|
||||||
interface ViewManagerProps { }
|
export class ViewManager extends React.Component<{}, {}> {
|
||||||
|
|
||||||
interface ViewManagerState { }
|
|
||||||
|
|
||||||
export class ViewManager extends React.Component<ViewManagerProps, ViewManagerState> {
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
deprecationWarning('As of @ionic/react RC2, ViewManager is no longer needed and can be removed. This component is now deprecated will be removed from @ionic/react final.')
|
deprecationWarning('As of @ionic/react RC2, ViewManager is no longer needed and can be removed. This component is now deprecated will be removed from @ionic/react final.');
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
import { Location as HistoryLocation } from 'history';
|
import { Location as HistoryLocation } from 'history';
|
||||||
import { ViewItem } from './ViewItem';
|
|
||||||
import { IonRouteData } from './IonRouteData';
|
|
||||||
import { matchPath } from 'react-router-dom';
|
import { matchPath } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { IonRouteData } from './IonRouteData';
|
||||||
|
import { ViewItem } from './ViewItem';
|
||||||
|
|
||||||
export interface ViewStack {
|
export interface ViewStack {
|
||||||
id: string;
|
id: string;
|
||||||
routerOutlet: HTMLIonRouterOutletElement;
|
routerOutlet: HTMLIonRouterOutletElement;
|
||||||
views: ViewItem[]
|
views: ViewItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The holistic view of all the Routes configured for an application inside of an IonRouterOutlet.
|
* The holistic view of all the Routes configured for an application inside of an IonRouterOutlet.
|
||||||
*/
|
*/
|
||||||
export class ViewStacks {
|
export class ViewStacks {
|
||||||
private viewStacks: { [key: string]: ViewStack } = {};
|
private viewStacks: { [key: string]: ViewStack | undefined } = {};
|
||||||
|
|
||||||
get(key: string) {
|
get(key: string) {
|
||||||
return this.viewStacks[key];
|
return this.viewStacks[key];
|
||||||
@ -31,12 +32,12 @@ export class ViewStacks {
|
|||||||
delete this.viewStacks[key];
|
delete this.viewStacks[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
findViewInfoByLocation(location: HistoryLocation, key?: string) {
|
findViewInfoByLocation(location: HistoryLocation, viewKey?: string) {
|
||||||
let view: ViewItem<IonRouteData> | undefined;
|
let view: ViewItem<IonRouteData> | undefined;
|
||||||
let match: IonRouteData["match"] | null | undefined;
|
let match: IonRouteData['match'] | null | undefined;
|
||||||
let viewStack: ViewStack | undefined;
|
let viewStack: ViewStack | undefined;
|
||||||
if (key) {
|
if (viewKey) {
|
||||||
viewStack = this.viewStacks[key];
|
viewStack = this.viewStacks[viewKey];
|
||||||
if (viewStack) {
|
if (viewStack) {
|
||||||
viewStack.views.some(matchView);
|
viewStack.views.some(matchView);
|
||||||
}
|
}
|
||||||
@ -44,7 +45,7 @@ export class ViewStacks {
|
|||||||
const keys = this.getKeys();
|
const keys = this.getKeys();
|
||||||
keys.some(key => {
|
keys.some(key => {
|
||||||
viewStack = this.viewStacks[key];
|
viewStack = this.viewStacks[key];
|
||||||
return viewStack.views.some(matchView);
|
return viewStack!.views.some(matchView);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +58,7 @@ export class ViewStacks {
|
|||||||
path: v.routeData.childProps.path || v.routeData.childProps.from,
|
path: v.routeData.childProps.path || v.routeData.childProps.from,
|
||||||
component: v.routeData.childProps.component
|
component: v.routeData.childProps.component
|
||||||
};
|
};
|
||||||
match = matchPath(location.pathname, matchProps)
|
match = matchPath(location.pathname, matchProps);
|
||||||
if (match) {
|
if (match) {
|
||||||
view = v;
|
view = v;
|
||||||
return true;
|
return true;
|
||||||
@ -67,13 +68,13 @@ export class ViewStacks {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
findViewInfoById(id: string = '') {
|
findViewInfoById(id = '') {
|
||||||
let view: ViewItem<IonRouteData> | undefined;
|
let view: ViewItem<IonRouteData> | undefined;
|
||||||
let viewStack: ViewStack | undefined;
|
let viewStack: ViewStack | undefined;
|
||||||
const keys = this.getKeys();
|
const keys = this.getKeys();
|
||||||
keys.some(key => {
|
keys.some(key => {
|
||||||
const vs = this.viewStacks[key];
|
const vs = this.viewStacks[key];
|
||||||
view = vs.views.find(x => x.id === id);
|
view = vs!.views.find(x => x.id === id);
|
||||||
if (view) {
|
if (view) {
|
||||||
viewStack = vs;
|
viewStack = vs;
|
||||||
return true;
|
return true;
|
||||||
@ -88,13 +89,12 @@ export class ViewStacks {
|
|||||||
const keys = this.getKeys();
|
const keys = this.getKeys();
|
||||||
keys.forEach(key => {
|
keys.forEach(key => {
|
||||||
const viewStack = this.viewStacks[key];
|
const viewStack = this.viewStacks[key];
|
||||||
viewStack.views.forEach(view => {
|
viewStack!.views.forEach(view => {
|
||||||
if (!view.routeData.match && !view.isIonRoute) {
|
if (!view.routeData.match && !view.isIonRoute) {
|
||||||
view.show = false;
|
view.show = false;
|
||||||
view.mount = false;
|
view.mount = false;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
import { DefaultIonLifeCycleContext, IonLifeCycleContext } from '@ionic/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { IonLifeCycleContext, DefaultIonLifeCycleContext } from '@ionic/react';
|
|
||||||
import { RouteManagerContext } from './RouteManagerContext';
|
import { RouteManagerContext } from './RouteManagerContext';
|
||||||
|
|
||||||
interface ViewTransitionManagerProps {
|
interface ViewTransitionManagerProps {
|
||||||
@ -20,7 +21,7 @@ export class ViewTransitionManager extends React.Component<ViewTransitionManager
|
|||||||
context!: React.ContextType<typeof RouteManagerContext>;
|
context!: React.ContextType<typeof RouteManagerContext>;
|
||||||
|
|
||||||
constructor(props: ViewTransitionManagerProps) {
|
constructor(props: ViewTransitionManagerProps) {
|
||||||
super(props)
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
show: true
|
show: true
|
||||||
};
|
};
|
||||||
@ -52,7 +53,7 @@ export class ViewTransitionManager extends React.Component<ViewTransitionManager
|
|||||||
<IonLifeCycleContext.Provider value={this.ionLifeCycleContext}>
|
<IonLifeCycleContext.Provider value={this.ionLifeCycleContext}>
|
||||||
{show && this.props.children}
|
{show && this.props.children}
|
||||||
</IonLifeCycleContext.Provider>
|
</IonLifeCycleContext.Provider>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static get contextType() {
|
static get contextType() {
|
||||||
|
@ -1 +1 @@
|
|||||||
export * from './ReactRouter'
|
export * from './ReactRouter';
|
||||||
|
14
packages/react-router/src/utils/__tests__/dev.spec.ts
Normal file
14
packages/react-router/src/utils/__tests__/dev.spec.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
import { isDevMode } from '../dev';
|
||||||
|
|
||||||
|
describe('isDevMode', () => {
|
||||||
|
it('by default, should return false since we are in test', () => {
|
||||||
|
const isDev = isDevMode();
|
||||||
|
expect(isDev).toBeFalsy();
|
||||||
|
});
|
||||||
|
it('when in dev mode, should return true', () => {
|
||||||
|
process.env.NODE_ENV = 'development';
|
||||||
|
const isDev = isDevMode();
|
||||||
|
expect(isDev).toBeTruthy()
|
||||||
|
});
|
||||||
|
});
|
@ -1,3 +1,32 @@
|
|||||||
{
|
{
|
||||||
"extends": "tslint-ionic-rules"
|
"extends": ["tslint-ionic-rules/strict", "tslint-react"],
|
||||||
|
"linterOptions": {
|
||||||
|
"exclude": [
|
||||||
|
"**/*.spec.ts",
|
||||||
|
"**/*.spec.tsx"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"no-conditional-assignment": false,
|
||||||
|
"no-non-null-assertion": false,
|
||||||
|
"no-unnecessary-type-assertion": false,
|
||||||
|
"no-import-side-effect": false,
|
||||||
|
"trailing-comma": false,
|
||||||
|
"no-null-keyword": false,
|
||||||
|
"no-console": false,
|
||||||
|
"no-unbound-method": false,
|
||||||
|
"no-floating-promises": false,
|
||||||
|
"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"],
|
||||||
|
"jsx-key": false,
|
||||||
|
"jsx-self-close": false,
|
||||||
|
"jsx-curly-spacing": [true, "never"],
|
||||||
|
"jsx-boolean-value": [true, "never"],
|
||||||
|
"jsx-no-bind": false,
|
||||||
|
"jsx-no-lambda": false,
|
||||||
|
"jsx-no-multiline-js": false,
|
||||||
|
"jsx-wrap-multiline": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
"lint.fix": "tslint --project . --fix",
|
"lint.fix": "tslint --project . --fix",
|
||||||
"tsc": "tsc -p .",
|
"tsc": "tsc -p .",
|
||||||
"copy": "node scripts/copy.js",
|
"copy": "node scripts/copy.js",
|
||||||
|
"test.spec": "jest --ci",
|
||||||
"test.treeshake": "node scripts/treeshaking.js dist/index.esm.js"
|
"test.treeshake": "node scripts/treeshaking.js dist/index.esm.js"
|
||||||
},
|
},
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { IonTabs, IonTabButton, IonLabel, IonIcon, IonTabBar} from '../index';
|
import { IonTabs, IonTabButton, IonLabel, IonIcon, IonTabBar} from '../index';
|
||||||
import { render, cleanup } from 'react-testing-library';
|
import { render, cleanup } from 'react-testing-library';
|
||||||
import { IonRouterOutlet } from '../proxies';
|
import { IonRouterOutlet } from '../IonRouterOutlet';
|
||||||
|
|
||||||
afterEach(cleanup)
|
afterEach(cleanup)
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ describe('createComponent - events', () => {
|
|||||||
|
|
||||||
test('should add custom events', () => {
|
test('should add custom events', () => {
|
||||||
const FakeIonFocus = jest.fn((e) => e);
|
const FakeIonFocus = jest.fn((e) => e);
|
||||||
const IonInput = createReactComponent<JSX.IonInput>('ion-input');
|
const IonInput = createReactComponent<JSX.IonInput, HTMLIonInputElement>('ion-input');
|
||||||
|
|
||||||
const { getByText } = render(
|
const { getByText } = render(
|
||||||
<IonInput onIonFocus={FakeIonFocus}>
|
<IonInput onIonFocus={FakeIonFocus}>
|
||||||
|
@ -69,8 +69,8 @@ export const isCoveredByReact = (eventNameSuffix: string, doc: Document = docume
|
|||||||
return isSupported;
|
return isSupported;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const syncEvent = (node: Element, eventName: string, newEventHandler: (e: Event) => any) => {
|
export const syncEvent = (node: Element & {__events?: {[key: string]: ((e: Event) => any) | undefined}}, eventName: string, newEventHandler?: (e: Event) => any) => {
|
||||||
const eventStore = (node as any).__events || ((node as any).__events = {});
|
const eventStore = node.__events || (node.__events = {});
|
||||||
const oldEventHandler = eventStore[eventName];
|
const oldEventHandler = eventStore[eventName];
|
||||||
|
|
||||||
// Remove old listener so they don't double up.
|
// Remove old listener so they don't double up.
|
||||||
|
2266
packages/react/test/test-app/package-lock.json
generated
2266
packages/react/test/test-app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,7 @@
|
|||||||
"@types/react-dom": "16.8.5",
|
"@types/react-dom": "16.8.5",
|
||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
"react-dom": "^16.8.6",
|
"react-dom": "^16.8.6",
|
||||||
"react-scripts": "^3.1.0",
|
"react-scripts": "^3.1.2",
|
||||||
"serve": "^11.1.0",
|
"serve": "^11.1.0",
|
||||||
"typescript": "3.5.3"
|
"typescript": "3.5.3"
|
||||||
},
|
},
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
"no-invalid-template-strings": true,
|
"no-invalid-template-strings": true,
|
||||||
"ban-export-const-enum": true,
|
"ban-export-const-enum": true,
|
||||||
"only-arrow-functions": true,
|
"only-arrow-functions": true,
|
||||||
|
"strict-boolean-conditions": [true, "allow-null-union", "allow-undefined-union", "allow-boolean-or-undefined", "allow-string"],
|
||||||
"jsx-key": false,
|
"jsx-key": false,
|
||||||
"jsx-self-close": false,
|
"jsx-self-close": false,
|
||||||
"jsx-curly-spacing": [true, "never"],
|
"jsx-curly-spacing": [true, "never"],
|
||||||
|
Reference in New Issue
Block a user