Skip to content

Commit 57e82ea

Browse files
author
Elmi Ahmadov
committed
Router: add example
1 parent 97621dd commit 57e82ea

File tree

8 files changed

+166
-86
lines changed

8 files changed

+166
-86
lines changed

examples/router/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Example "router"
2+
3+
[![GitPod Logo](../../doc/run-in-gitpod.png)](https://gitpod.io/#example=router/https://github.com/eclipsesource/tabris-decorators/tree/gplink/examples/router)
4+
5+
## Description
6+
7+
Demonstrates the usage of the router API.

examples/router/package.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "router",
3+
"version": "3.3.0",
4+
"dependencies": {
5+
"reflect-metadata": "^0.1.13"
6+
},
7+
"optionalDependencies": {
8+
"tabris": "^3.3.0",
9+
"tabris-decorators": "3.3.0"
10+
},
11+
"devDependencies": {
12+
"typescript": "3.3.x"
13+
},
14+
"main": "dist/app.js",
15+
"scripts": {
16+
"start": "tabris serve -w -a",
17+
"build": "tsc -p .",
18+
"watch": "tsc -p . -w --preserveWatchOutput --inlineSourceMap",
19+
"gitpod": "tabris serve -a -w --no-intro --port 8080 --external $(gp url 8080):443"
20+
}
21+
}

examples/router/src/app.tsx

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import {NavigationView, contentView, Button, TextView, TextInput, Stack, Page, Properties} from 'tabris';
2+
import {Router, Route, injectable, create, resolve, property, component } from 'tabris-decorators';
3+
4+
const navigationView = new NavigationView({
5+
layoutData: 'stretch'
6+
}).appendTo(contentView);
7+
8+
class MyPage1 extends Page {
9+
constructor(properties?: Properties<MyPage1>) {
10+
super(properties);
11+
this.append(
12+
<Stack stretch alignment="stretchX" padding={[0, 4]}>
13+
<TextInput/>
14+
<Button text="Open" onSelect={() =>
15+
router.goTo({
16+
route: 'MyRoute2',
17+
payload: {
18+
text: this.find(TextInput).only().text
19+
}
20+
})}
21+
/>
22+
</Stack>
23+
);
24+
}
25+
}
26+
27+
@component
28+
class MyPage2 extends Page {
29+
@property text: string;
30+
31+
constructor(properties?: Properties<MyPage2>) {
32+
super(properties);
33+
this.append(
34+
<Stack stretch alignment="stretchX" padding={[0, 4]}>
35+
<TextView alignment="centerX" height={32} bind-text="text"/>
36+
<Button text="Go back" onSelect={() => router.back()}/>
37+
<TextInput bind-text="text" />
38+
<Button text="Open" onSelect={() =>
39+
router.goTo({
40+
route: 'MyRoute2',
41+
payload: {
42+
text: this._find(TextInput).only().text
43+
}
44+
})}
45+
/>
46+
</Stack>
47+
);
48+
}
49+
}
50+
51+
@injectable({ param: 'MyRoute1' })
52+
class MyRoute1 extends Route {
53+
page = new MyPage1({title: 'foo'});
54+
};
55+
56+
@injectable({ param: 'MyRoute2' })
57+
class MyRoute2 extends Route {
58+
page = new MyPage2({title: 'bar'});
59+
};
60+
61+
const router = new Router({
62+
navigationView,
63+
history: [{ route: 'MyRoute1' }]
64+
});

examples/router/tsconfig.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"compilerOptions": {
3+
"module": "commonjs",
4+
"target": "es6",
5+
"lib": ["es6" ],
6+
"jsx": "react",
7+
"jsxFactory": "JSX.createElement",
8+
"outDir": "dist",
9+
"experimentalDecorators": true,
10+
"emitDecoratorMetadata": true,
11+
"typeRoots": ["./node_modules/@types"]
12+
},
13+
"include": [
14+
"./src/*.ts",
15+
"./src/*.tsx"
16+
]
17+
}

src/api/router/Route.ts

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,8 @@
1-
import { Page, Properties } from "tabris";
1+
import { Page } from "tabris";
22

3-
export interface RouteOptions {
4-
enableDrawer?: boolean;
5-
toolbarVisible?: boolean;
6-
}
7-
8-
export interface RoutePage {
9-
onPayload?(payload?: any): void;
10-
}
11-
12-
export abstract class RoutePage extends Page {
13-
constructor(properties?: Properties<RoutePage>) {
14-
super(properties);
15-
}
16-
}
3+
export type Dictionary<T> = { [key: string]: T };
174

18-
export abstract class Route {
19-
page: RoutePage;
20-
options: RouteOptions = {
21-
enableDrawer: false,
22-
toolbarVisible: true
23-
}
5+
export class Route<PayloadType = Dictionary<string>> {
6+
page: Page;
7+
payload?: PayloadType;
248
}

src/api/router/Router.ts

Lines changed: 12 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,24 @@ import { ListLike, Mutation } from "../List";
33
import { RouterMatcher } from "./RouterMatcher";
44
import { RouterHistory, HistoryItem } from "./RouterHistory";
55
import { Route } from "./Route";
6-
import { Constructor } from "../../internals/utils";
7-
8-
export type RouterConfig = {
9-
name: string;
10-
route: Constructor<Route>
11-
};
126

137
export type RouterProperties = {
148
navigationView: NavigationView,
15-
routers?: ListLike<RouterConfig>,
169
defaultRoute?: HistoryItem,
1710
history?: ListLike<HistoryItem>
1811
};
1912

2013
export class Router<ItemType extends HistoryItem = HistoryItem> {
2114

2215
private _navigationView: NavigationView;
23-
private _routes: ListLike<RouterConfig>;
24-
2516
private _routerHistoryObserver: RouterHistory;
2617
private _routerMatcher: RouterMatcher;
2718

28-
constructor({navigationView, routers, defaultRoute, history} : RouterProperties) {
19+
constructor({navigationView, history} : RouterProperties) {
2920
this._navigationView = navigationView;
30-
this.routes = routers || [];
3121
this._routerHistoryObserver = new RouterHistory(this._handleHistoryChange);
22+
this._routerMatcher = new RouterMatcher();
3223
this.history = history || [];
33-
this._routerMatcher = new RouterMatcher(this);
34-
if (defaultRoute) {
35-
this.goTo(defaultRoute as ItemType);
36-
}
3724
this._navigationView.onRemoveChild(this._syncHistoryWithNavigationView.bind(this));
3825
}
3926

@@ -42,33 +29,18 @@ export class Router<ItemType extends HistoryItem = HistoryItem> {
4229
}
4330

4431
back() {
45-
if (this._routerHistoryObserver.source.length === 0) {
32+
if (this._routerHistoryObserver.history.length === 0) {
4633
throw new Error("Could not call back on empty history stack");
4734
}
4835
this._routerHistoryObserver.pop();
4936
}
5037

5138
set history(value: ListLike<HistoryItem>) {
52-
if (this._routerHistoryObserver.source === value) {
53-
return;
54-
}
55-
this._routerHistoryObserver.source = value;
39+
this._routerHistoryObserver.history = value;
5640
}
5741

5842
get history() {
59-
return this._routerHistoryObserver.source;
60-
}
61-
62-
set routes(value: ListLike<RouterConfig>) {
63-
if (this._routes === value) {
64-
return;
65-
}
66-
this._routes = value;
67-
this._routerMatcher = new RouterMatcher(this);
68-
}
69-
70-
get routes() {
71-
return this._routes;
43+
return this._routerHistoryObserver.history;
7244
}
7345

7446
protected _handleHistoryChange = ({deleteCount, items}: Mutation<ItemType>) => {
@@ -97,13 +69,15 @@ export class Router<ItemType extends HistoryItem = HistoryItem> {
9769
});
9870
}
9971

100-
private _appendRoute(route: Route, payload?: any) {
101-
if (route.page.onPayload && typeof route.page.onPayload === 'function') {
102-
route.page.onPayload(payload);
72+
private _appendRoute(route: Route, payload?: object) {
73+
if (payload) {
74+
for (const key of Object.keys(payload)) {
75+
if (key in route.page) {
76+
route.page[key] = payload[key];
77+
}
78+
}
10379
}
10480
this._navigationView.append(route.page);
105-
this._navigationView.drawerActionVisible = route.options.enableDrawer;
106-
this._navigationView.toolbarVisible = route.options.toolbarVisible;
10781
}
10882

10983
private _syncHistoryWithNavigationView() {

src/api/router/RouterHistory.ts

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,53 @@
11
import { ListLikeObvserver } from "../../internals/ListLikeObserver";
2+
import { Mutation, List, ListLike } from "../List";
3+
import { Dictionary } from "./Route";
24

3-
export type HistoryItem = { route: string, payload?: any };
5+
export type HistoryItem = { route: string, payload?: Dictionary<string> };
46

5-
export class RouterHistory<T extends HistoryItem = HistoryItem> extends ListLikeObvserver<T> {
7+
export class RouterHistory<T extends HistoryItem = HistoryItem> {
8+
9+
private _observer: ListLikeObvserver<T>;
10+
11+
constructor(_callback: (ev: Mutation<T>) => void) {
12+
this._observer = new ListLikeObvserver<T>(_callback);
13+
}
614

715
public push(item: T) {
8-
const source = Array.from(this.source);
16+
const source = this.getSource();
917
source.push(item);
10-
this.source = source;
18+
this._observer.source = source;
1119
}
1220

1321
public pop(): T {
14-
const source = Array.from(this.source);
22+
const source = this.getSource();
1523
const result = source.pop();
16-
this.source = source;
24+
this._observer.source = source;
1725
return result;
1826
}
1927

2028
public removeLast() {
21-
return this.source.pop();
29+
return this._observer.source.pop();
30+
}
31+
32+
get history() {
33+
return this._observer.source;
34+
}
35+
36+
set history(value: ListLike<T>) {
37+
this._observer.source = value;
2238
}
2339

2440
get current() {
25-
return this.source[this.source.length - 1];
41+
return this._observer.source[this._observer.source.length - 1];
42+
}
43+
44+
private getSource() {
45+
if (this._observer.source instanceof Array) {
46+
return Array.from(this._observer.source);
47+
} else if (this._observer.source instanceof List) {
48+
return List.from(this._observer.source);
49+
}
50+
throw new Error('Unsupported type');
2651
}
2752

2853
}

src/api/router/RouterMatcher.ts

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,18 @@
1-
import { Router, RouterConfig } from "./Router";
21
import { Route } from "./Route";
32
import { HistoryItem } from './RouterHistory';
4-
import { Constructor } from "../../internals/utils";
3+
import { resolve } from "../..";
54

65
export class RouterMatcher {
7-
private _nameMap: Map<string, RouterConfig> = new Map();
8-
9-
constructor(router: Router) {
10-
const routes = router.routes || [];
11-
routes.forEach(item => {
12-
if (this._nameMap.has(item.name)) {
13-
throw new Error(`Route with '${item.name}' name already exists!`);
14-
}
15-
this._nameMap.set(item.name, item);
16-
});
17-
}
18-
196
match(historyItem: HistoryItem): Route {
207
const name = historyItem.route;
21-
if (this._nameMap.has(name)) {
22-
return this._createRoute(this._nameMap.get(name).route);
8+
const route = this._createRoute(name);
9+
if (!route) {
10+
throw new Error(`Route with '${name}' name does not exist!`);
2311
}
24-
throw new Error(`Route with '${name}' name does not exist!`);
12+
return route;
2513
}
2614

27-
private _createRoute(className: Constructor<Route>): Route {
28-
return new className(); // TODO: support to pass parameters
15+
private _createRoute(name: string): Route {
16+
return resolve(Route, name);
2917
}
3018
}

0 commit comments

Comments
 (0)