Skip to content

Commit

Permalink
[refactor] support Routes Sequence & Async Pages
Browse files Browse the repository at this point in the history
  • Loading branch information
TechQuery committed Jan 6, 2020
1 parent 2d43988 commit 75c1c29
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 47 deletions.
86 changes: 76 additions & 10 deletions ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ https://web-cell.dev/scaffold/

- [x] **Path Mode**: `location.hash` (default) & `history.pushState()`

- [x] **Async Loading** (recommend to use with `import()` ECMAScript proposal)

- [x] (experimental) [Nested Router][5] support

## Installation
Expand All @@ -46,6 +48,8 @@ npm install parcel-bundler -D

## Usage

### Sync Pages

`source/model/index.ts`

```typescript
Expand All @@ -56,7 +60,7 @@ export const history = new History();

`source/page/PageRouter.tsx`

```jsx
```javascript
import { createCell, component } from 'web-cell';
import { observer } from 'mobx-web-cell';
import { HTMLRouter } from 'cell-router/source';
Expand All @@ -78,6 +82,10 @@ function Example({ path }) {
})
export default class PageRouter extends HTMLRouter {
protected history = history;
protected routes = [
{ paths: ['test'], component: Test },
{ paths: ['example'], component: Example }
];

render() {
return (
Expand All @@ -90,15 +98,73 @@ export default class PageRouter extends HTMLRouter {
<a href="example">Example</a>
</li>
</ul>
<div>
{matchRoutes(
[
{ paths: ['test'], component: Test },
{ paths: ['example'], component: Example }
],
history.path
)}
</div>
<div>{super.render()}</div>
</main>
);
}
}
```

### Async Pages

`tsconfig.json`

```json
{
"compilerOptions": {
"module": "ESNext"
}
}
```

`source/page/index.ts`

```javascript
export default [
{
paths: ['', 'home'],
component: async () => (await import('./Home.tsx')).default,
async: true
},
{
paths: ['list'],
component: async () => (await import('./List.tsx')).default,
async: true
}
];
```

`source/component/PageRouter.tsx`

```javascript
import { component, createCell } from 'web-cell';
import { observer } from 'mobx-web-cell';
import { HTMLRouter } from 'cell-router/source';

import { history } from '../model';
import routes from '../page';

@observer
@component({
tagName: 'page-router',
renderTarget: 'children'
})
export default class PageRouter extends HTMLRouter {
protected history = history;
protected routes = routes;

render() {
return (
<main>
<ul>
<li>
<a href="home">Home</a>
</li>
<li>
<a href="list">List</a>
</li>
</ul>
<div>{super.render()}</div>
</main>
);
}
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cell-router",
"version": "2.0.0-rc.3",
"version": "2.0.0-rc.4",
"license": "LGPL-3.0",
"description": "Web Component Router based on WebCell & MobX",
"keywords": [
Expand All @@ -26,7 +26,7 @@
"peerDependencies": {
"mobx": "^5.15.1",
"mobx-web-cell": "^0.2.5",
"web-cell": "^2.0.0-rc.12"
"web-cell": "^2.0.0-rc.13"
},
"devDependencies": {
"@types/jest": "^24.0.25",
Expand All @@ -48,10 +48,10 @@
"ts-jest": "^24.2.0",
"typedoc": "^0.15.6",
"typescript": "^3.7.4",
"web-cell": "^2.0.0-rc.12"
"web-cell": "^2.0.0-rc.13"
},
"scripts": {
"debug": "cd test/ && parcel source/index.html",
"start": "cd test/ && parcel source/index.html --open",
"lint": "lint-staged",
"pack-test": "cd test/ && parcel build source/index.html",
"set-chrome": "app-find chrome -c",
Expand Down
39 changes: 37 additions & 2 deletions source/HTMLRouter.ts → source/HTMLRouter.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { mixin, delegate } from 'web-cell';
import { mixin, delegate, createCell } from 'web-cell';

import { History, HistoryMode } from './History';
import { scrollTo } from './utility';
import { Route, scrollTo, matchRoutes } from './utility';

type LinkElement = HTMLAnchorElement | HTMLAreaElement;

Expand All @@ -25,6 +25,8 @@ export abstract class HTMLRouter extends mixin() {
}

protected abstract history: History;
protected abstract routes: Route[];
private currentPage: Function;

handleLink = delegate('a[href]', (event: MouseEvent) => {
const link = event.target as LinkElement;
Expand Down Expand Up @@ -63,6 +65,15 @@ export abstract class HTMLRouter extends mixin() {

this.history.reset(!!this.parentRouter);

this.routes = this.routes
.map(({ paths, ...rest }) =>
paths.map(path => ({ paths: [path], ...rest }))
)
.flat()
.sort(({ paths: [A] }, { paths: [B] }) =>
(B as string).localeCompare(A as string)
);

this.addEventListener('click', this.handleLink);
window.addEventListener('popstate', this.handleBack);

Expand All @@ -77,4 +88,28 @@ export abstract class HTMLRouter extends mixin() {
if (this.history.mode === HistoryMode.hash)
window.removeEventListener('hashchange', this.handleHash);
}

render() {
const { component, async, path, params } =
matchRoutes(this.routes, this.history.path) || {};

if (!component) return;

if (async)
component().then((func: Function) => {
const route = this.routes.find(
route => route.component === component
);
if (!route) return;

route.component = func;
delete route.async;
// @ts-ignore
this.update();
});
else this.currentPage = component;

if (this.currentPage)
return <this.currentPage {...params} path={path} />;
}
}
16 changes: 6 additions & 10 deletions source/utility.tsx → source/utility.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { createCell } from 'web-cell';

export function parsePathData(URI: string) {
const params = {},
[path, data] = URI.split('?');
Expand Down Expand Up @@ -31,21 +29,19 @@ export function scrollTo(selector: string, root?: Element) {
if (anchor) anchor.scrollIntoView({ behavior: 'smooth' });
}

interface Route {
export interface Route {
paths: (string | RegExp)[];
component: Function;
component: Function | (() => Promise<Function>);
async?: boolean;
}

export function matchRoutes(list: Route[], path: string) {
for (const { paths, component: Component } of list)
for (const { paths, ...rest } of list)
for (const item of paths)
if (
typeof item === 'string'
? path.startsWith(item)
: item.exec(path)
) {
const data = parsePathData(path);

return <Component {...data.params} path={data.path} />;
}
)
return { ...rest, ...parsePathData(path) };
}
16 changes: 6 additions & 10 deletions test/source/page/SubRouter.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createCell, component } from 'web-cell';
import { observer } from 'mobx-web-cell';
import { HTMLRouter, matchRoutes } from '../../../source';
import { HTMLRouter } from '../../../source';

import { subHistory } from '../model';

Expand All @@ -19,6 +19,10 @@ function Temp({ path }) {
})
export default class SubRouter extends HTMLRouter {
protected history = subHistory;
protected routes = [
{ paths: ['sample'], component: Sample },
{ paths: ['temp'], component: Temp }
];

render() {
return (
Expand All @@ -31,15 +35,7 @@ export default class SubRouter extends HTMLRouter {
<a href="temp">Temp</a>
</li>
</ul>
<div>
{matchRoutes(
[
{ paths: ['sample'], component: Sample },
{ paths: ['temp'], component: Temp }
],
subHistory.path
)}
</div>
<div>{super.render()}</div>
</main>
);
}
Expand Down
20 changes: 10 additions & 10 deletions test/source/page/TopRouter.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createCell, component } from 'web-cell';
import { observer } from 'mobx-web-cell';
import { HTMLRouter, matchRoutes } from '../../../source';
import { HTMLRouter } from '../../../source';

import { topHistory } from '../model';
import SubRouter from './SubRouter';
Expand All @@ -25,6 +25,14 @@ function Example({ path }) {
})
export default class TopRouter extends HTMLRouter {
protected history = topHistory;
protected routes = [
{ paths: ['test'], component: Test },
{
paths: ['example'],
component: () => Promise.resolve(Example),
async: true
}
];

render() {
return (
Expand All @@ -37,15 +45,7 @@ export default class TopRouter extends HTMLRouter {
<a href="example">Example</a>
</li>
</ul>
<div>
{matchRoutes(
[
{ paths: ['test'], component: Test },
{ paths: ['example'], component: Example }
],
topHistory.path
)}
</div>
<div>{super.render()}</div>
</main>
);
}
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
"compilerOptions": {
"module": "ES6",
"moduleResolution": "Node",
"esModuleInterop": true,
"downlevelIteration": true,
"experimentalDecorators": true,
"jsx": "react",
"jsxFactory": "createCell",
"lib": ["DOM", "DOM.Iterable"],
"lib": ["ES2019", "DOM", "DOM.Iterable"],
"target": "ES5"
},
"include": ["source/**/*"]
Expand Down

0 comments on commit 75c1c29

Please sign in to comment.