Skip to content

Refactored History management #31

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Dec 15, 2022
5 changes: 5 additions & 0 deletions .changeset/violet-years-walk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@chialab/synapse": patch
---

Introduce the `Path` object for routing.
52 changes: 52 additions & 0 deletions examples/navigation/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { html, customElements } from '@chialab/dna';
import { App, DocumentMetaMiddleware } from '@chialab/synapse';
import { Link } from './Elements/Link.js';
import { Dashboard } from './Pages/Dashboard.js';
import { Team } from './Pages/Team.js';
import { Projects } from './Pages/Projects.js';
import { NotFound } from './Pages/NotFound.js';

export class DemoApp extends App {
routes = [
new Dashboard({ pattern: '/' }),
new Team({ pattern: '/team' }),
new Projects({ pattern: '/projects' }),
new NotFound({ pattern: '*' }),
];

middlewares = [
new DocumentMetaMiddleware(),
];

render() {
return html`<div class="min-h-full flex flex-col">
<nav class="bg-gray-800 flex-none">
<div class="px-4">
<div class="flex h-16 items-center justify-between">
<div class="flex items-center">
<div class="flex items-baseline space-x-4">
<${Link} router=${this.router} href="/">Dashboard</>
<${Link} router=${this.router} href="/team">Team</>
<${Link} router=${this.router} href="/projects">Projects</>
</div>
</div>
</div>
</div>
</nav>
<main class="bg-gray-100 flex-auto">
<header class="bg-white shadow">
<div class="mx-auto max-w-7xl py-6 px-4 sm:px-6 lg:px-8">
<h1 class="text-3xl font-bold tracking-tight text-gray-900">${this.response?.title}</h1>
</div>
</header>
<div class="mx-auto max-w-7xl py-6 sm:px-6 lg:px-8">
<div class="px-4 py-6 sm:px-0">
${super.render()}
</div>
</div>
</main>
</div>`;
}
}

customElements.define('demo-app', DemoApp);
21 changes: 21 additions & 0 deletions examples/navigation/Elements/Link.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { html } from '@chialab/dna';

export function Link({ children, router, href }) {
const isCurrentPage = href === router.current;

return html`<a
href=${router.resolve(href)}
class=${{
'bg-gray-900': isCurrentPage,
'text-white': isCurrentPage,
'text-gray-300': !isCurrentPage,
'hover:bg-gray-700': !isCurrentPage,
'hover:text-white': !isCurrentPage,
'px-3': true,
'py-2': true,
'rounded-md': true,
'text-sm': true,
'font-medium': true,
}}
aria-current=${isCurrentPage ? 'page' : ''}>${children}</a>`;
}
20 changes: 20 additions & 0 deletions examples/navigation/Pages/Dashboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { html } from '@chialab/dna';
import { Route } from '@chialab/synapse';

export class Dashboard extends Route {
async exec(request, response) {
response.setTitle('Dashboard');
const { createdAt } = response.getData({
createdAt: null,
});
response.setData({
pristine: createdAt === null,
createdAt: createdAt ?? Date.now(),
});
}

view = (request, response) => html`
<p>This is the dashboard page.</p>
<p>${response.getData().pristine ? 'This page is newly created ✨' : 'This page has been recycled ♻️'}</p>
`;
}
12 changes: 12 additions & 0 deletions examples/navigation/Pages/NotFound.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { html } from '@chialab/dna';
import { Route } from '@chialab/synapse';

export class NotFound extends Route {
async exec(request, response) {
response.setTitle('Not found');

response.setView(() => html`
Page not found.
`);
}
}
12 changes: 12 additions & 0 deletions examples/navigation/Pages/Projects.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { html } from '@chialab/dna';
import { Route } from '@chialab/synapse';

export class Projects extends Route {
async exec(request, response) {
response.setTitle('Projects');

response.setView(() => html`
Projects.
`);
}
}
15 changes: 15 additions & 0 deletions examples/navigation/Pages/Team.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { html } from '@chialab/dna';
import { Route } from '@chialab/synapse';

export class Team extends Route {
async exec(request, response) {
response.setTitle('Team');

response.setView(() => html`<ul class="list-disc">
<li>Alan</li>
<li>Bart</li>
<li>Carl</li>
<li>Denis</li>
</ul>`);
}
}
41 changes: 41 additions & 0 deletions examples/navigation/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Synapse</title>
<script src="https://cdn.tailwindcss.com"></script>
<script type="importmap">
{
"imports": {
"@chialab/dna": "/node_modules/@chialab/dna/dist/esm/dna.js",
"@chialab/dna/jsx-runtime": "/node_modules/@chialab/dna/jsx-runtime.js",
"@chialab/synapse": "/dist/esm/synapse.js"
}
}
</script>
<script type="module" src="index.js"></script>
<style>
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}

body {
display: flex;
}

demo-app {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
</body>
</html>
11 changes: 11 additions & 0 deletions examples/navigation/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { html, render, document } from '@chialab/dna';
import { BrowserHistory } from '@chialab/synapse';
import './App.js';

const app = render(html`<demo-app
base="/examples/navigation/#!/"
history=${new BrowserHistory()}

/>`, document.body);

app.start();
20 changes: 9 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
},
"types": "types/index.d.ts",
"exports": {
"require": "./dist/cjs/synapse.cjs",
"node": "./dist/node/synapse.js",
"default": "./dist/esm/synapse.js"
".": {
"require": "./dist/cjs/synapse.cjs",
"node": "./dist/node/synapse.js",
"default": "./dist/esm/synapse.js"
}
},
"scripts": {
"build": "rimraf dist && yarn types && rna build",
Expand All @@ -37,24 +39,20 @@
"author": "Chialab <[email protected]> (https://www.chialab.it)",
"dependencies": {
"@chialab/dna": "^3.20.0",
"@chialab/proteins": "^3.2.2",
"node-fetch": "^3.0.0",
"tslib": "^2.0.0"
},
"devDependencies": {
"@changesets/cli": "^2.22.0",
"@chialab/eslint-config": "^3.0.0",
"@chialab/ginsenghino": "^1.1.0",
"@chialab/rna": "^0.16.0",
"@chialab/rna-browser-test-runner": "^0.16.1",
"@chialab/rna-bundler": "^0.16.0",
"@chialab/rna-node-test-runner": "^0.16.0",
"@chialab/rna": "^0.17.0",
"@chialab/rna-browser-test-runner": "^0.17.1",
"@chialab/rna-bundler": "^0.17.0",
"@chialab/rna-node-test-runner": "^0.17.0",
"eslint": "^8.0.0",
"jsdom": "^16.0.0",
"rimraf": "^3.0.2",
"typescript": "^4.1.3"
},
"resolutions": {
"esbuild": "0.14.39"
}
}
5 changes: 2 additions & 3 deletions rna.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ const config = {
},
],
minify: true,
jsxFactory: 'h',
jsxFragment: 'Fragment',
jsxModule: '@chialab/dna',
jsx: 'automatic',
jsxImportSource: '@chialab/dna',
};

export default config;
53 changes: 34 additions & 19 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import type { PopStateData } from './Router/Router';
import type { Route } from './Router/Route';
import type { Middleware } from './Router/Middleware';
import type { State } from './Router/State';
import type { RequestInit, RequestMethod } from './Router/Request';
import { Component, window, property, state, observe, listen, customElementPrototype } from '@chialab/dna';
import { Request } from './Router/Request';
import { Response } from './Router/Response';
import { Router, DEFAULT_ORIGIN } from './Router/Router';
import { NavigationDirection, History } from './Router/History';
import { Page } from './Components/Page';

enum NavigationDirection {
back = 'back',
forward = 'forward',
}

/**
* A Web Component which handles routing.
*/
Expand Down Expand Up @@ -82,7 +80,7 @@ export class App extends Component {
/**
* The History instance for the application.
*/
public history: History = window.history;
public history: History = new History();

/**
* The Router instance for the application.
Expand All @@ -92,6 +90,16 @@ export class App extends Component {
base: this.base,
});

/**
* Routes to connect.
*/
public routes: Route[] = [];

/**
* Middlewares to connect.
*/
public middlewares: Middleware[] = [];

/**
* @inheritdoc
*/
Expand All @@ -118,17 +126,23 @@ export class App extends Component {
* @param path The initial path to navigate.
*/
async start(path?: string): Promise<Response | void> {
const { router, history, _onPopState } = this;
const { router, history, routes, middlewares } = this;
routes.forEach((route) => {
router.connect(route);
});
middlewares.forEach((middleware) => {
router.middleware(middleware);
});
router.middleware({
pattern: '*',
priority: -Infinity,
before: (req) => {
this.request = req;
},
});
router.on('popstate', _onPopState);
router.on('pushstate', _onPopState);
router.on('replacestate', _onPopState);
router.on('popstate', this._onPopState);
router.on('pushstate', this._onPopState);
router.on('replacestate', this._onPopState);

const response = await router.start(history, path);
if (response) {
Expand All @@ -140,7 +154,7 @@ export class App extends Component {
/**
* Trigger a routing navigation.
* @param path The route path to navigate.
* @return The response instance for the navigation.
* @returns The response instance for the navigation.
*/
navigate(path: string, init?: RequestInit): Promise<Response|null> {
return this.router.navigate(path, init);
Expand All @@ -149,7 +163,7 @@ export class App extends Component {
/**
* Replace current navigation.
* @param path The route path to navigate.
* @return The response instance for the navigation.
* @returns The response instance for the navigation.
*/
replace(path: string, init?: RequestInit): Promise<Response|null> {
return this.router.replace(path, init);
Expand Down Expand Up @@ -248,7 +262,10 @@ export class App extends Component {
* Popstate listener.
* @param data Popstate data.
*/
protected _onPopState = (data: PopStateData) => {
protected _onPopState = (data: { state: State; previous?: State }) => {
if (!data.state) {
return;
}
this.request = data.state.request;
this.onPopState(data);
this.response = data.state.response;
Expand All @@ -258,12 +275,10 @@ export class App extends Component {
* Handle popstate event from the router.
* @param data The event triggered by the router.
*/
onPopState(data: PopStateData) {
onPopState(data: { state: State; previous?: State }) {
const { state, previous } = data;
if (previous) {
this.navigationDirection = state.index < previous.index ?
NavigationDirection.back :
NavigationDirection.forward;
if (state && previous) {
this.navigationDirection = this.history.compareStates(previous, state);
} else {
this.navigationDirection = NavigationDirection.forward;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Helpers/Animations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { window } from '@chialab/dna';
/**
* requestAnimationFrame wrapper for support in Node environments.
* @param callback The callback to invoke.
* @return The numeric handle of the request.
* @returns The numeric handle of the request.
*/
export function requestAnimationFrame(callback: FrameRequestCallback): number {
if (typeof window.requestAnimationFrame === 'function') {
Expand Down
Loading