Skip to content
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

Refactored routing #69

Merged
merged 1 commit into from
Jan 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/lib/zipWith.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Zips two arrays together with a function.
* @param xs The first array.
* @param ys The second array.
* @param f The function to zip the arrays with.
* @returns An array of the merged values.
*/
export default <X, Y, Z>(xs: X[], ys: Y[], f: (x: X, y: Y) => Z) =>
xs.map((x, i) => f(x, ys[i]));
77 changes: 53 additions & 24 deletions src/presentation/Application.mts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type Page from './pages/Page.mjs';
import Router from './Router.mjs';
import NotFoundPage from './pages/NotFoundPage.mjs';
import html from './lib/html.mjs';
import { Breadcrumb, Container, GlobalNav } from '~components/index.mjs';

Expand All @@ -8,16 +8,14 @@ export default class Application extends Container {
customElements.define('x-application', this);
}
#currentPage: Page | null = null;
#router!: Router;
#pages!: typeof Page[];

constructor() {
super({}, []);

document.body.innerHTML = '';
this._installOrUpdateServiceWorker();
this._initRouter().then(() => {
self.navigation.navigate(location.pathname);
});
this._initPages().then(() => self.navigation.navigate(location.href));
}

protected override _initShadowHtml() {
Expand Down Expand Up @@ -62,25 +60,27 @@ export default class Application extends Container {
};
}

protected async _initRouter() {
this.#router = new Router([
['/', (await import('./pages/Home.mjs')).Home],
['/not-found', (await import('./pages/NotFound.mjs')).NotFound],
['/projects', (await import('./pages/projects/Projects.mjs')).Projects],
['/environments', (await import('./pages/environments/Environments.mjs')).Environments],
['/environments/new-entry', (await import('./pages/environments/NewEnvironment.mjs')).NewEnvironment],
['/environments/:slug', (await import('./pages/environments/Environment.mjs')).Environment],
['/environments/:slug/glossary', (await import('./pages/environments/Glossary.mjs')).Glossary],
['/goals', (await import('./pages/goals/Goals.mjs')).Goals],
['/goals/new-entry', (await import('./pages/goals/NewGoals.mjs')).NewGoals],
['/goals/:slug', (await import('./pages/goals/Goal.mjs')).Goal],
['/goals/:slug/rationale', (await import('./pages/goals/Rationale.mjs')).Rationale],
['/goals/:slug/functionality', (await import('./pages/goals/Functionality.mjs')).Functionality],
['/goals/:slug/stakeholders', (await import('./pages/goals/Stakeholders.mjs')).Stakeholders],
['/goals/:slug/use-cases', (await import('./pages/goals/UseCases.mjs')).UseCases],
['/goals/:slug/limitations', (await import('./pages/goals/Limitations.mjs')).Limitations],
]);
this.#router.addEventListener('route', this);
protected async _initPages() {
this.#pages = [
(await import('./pages/HomePage.mjs')).default,
NotFoundPage,
(await import('./pages/projects/ProjectsPage.mjs')).default,
(await import('./pages/environments/NewEnvironmentPage.mjs')).default,
(await import('./pages/environments/EnvironmentPage.mjs')).default,
(await import('./pages/environments/EnvironmentsPage.mjs')).default,
(await import('./pages/environments/GlossaryPage.mjs')).default,
(await import('./pages/goals/NewGoalsPage.mjs')).default,
(await import('./pages/goals/GoalPage.mjs')).default,
(await import('./pages/goals/GoalsPage.mjs')).default,
(await import('./pages/goals/RationalePage.mjs')).default,
(await import('./pages/goals/FunctionalityPage.mjs')).default,
(await import('./pages/goals/StakeholdersPage.mjs')).default,
(await import('./pages/goals/UseCasesPage.mjs')).default,
(await import('./pages/goals/LimitationsPage.mjs')).default,
];

self.navigation.addEventListener('navigate', this);
this.addEventListener('route', this);
}

protected async _installServiceWorker() {
Expand Down Expand Up @@ -122,6 +122,35 @@ export default class Application extends Container {
}
}

onNavigate(event: NavigateEvent): void {
if (!event.canIntercept || event.hashChange)
return;

const origin = document.location.origin,
url = new URL(event.destination.url, origin),
Page = this.#pages.find(Page => {
const route = Page.route,
pattern = route.split('/'),
pathname = url.pathname.split('/');

if (pattern.length !== pathname.length)
return false;

return pattern.every((segment, index) => {
if (segment.startsWith(':'))
return true;

return segment === pathname[index];
});
}) ?? NotFoundPage;
event.intercept({
handler: async () => {
event.preventDefault();
this.dispatchEvent(new CustomEvent('route', { detail: Page }));
}
});
}

onRoute(event: CustomEvent<typeof Page>) {
const Cons = event.detail;
this.#currentPage = new Cons({}, []);
Expand Down
63 changes: 0 additions & 63 deletions src/presentation/Router.mts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import Page from './Page.mjs';

const { p } = html;

export class Home extends Page {
export default class HomePage extends Page {
static override route = '/';
static {
customElements.define('x-page-home', this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import html from '../lib/html.mjs';

const { h1, p, a } = html;

export class NotFound extends Page {
export default class NotFoundPage extends Page {
static override route = '/not-found';
static {
customElements.define('x-page-not-found', this);
}
Expand Down
21 changes: 15 additions & 6 deletions src/presentation/pages/Page.mts
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
import zipWith from '~/lib/zipWith.mjs';
import type { Properties } from '~/types/Properties.mjs';
import buttonTheme from '~/presentation/theme/buttonTheme.mjs';
import { Container } from '~components/index.mjs';
import type { Theme } from '~/types/Theme.mjs';

export default class Page extends Container {
protected override _initShadowStyle() {
return {
...super._initShadowStyle()
};
}
static route = '{undefined}';

constructor(properties: Properties<Page>, children: (Element | string)[]) {
urlParams;

constructor(properties: Exclude<Properties<Page>, 'urlParams'>, children: (Element | string)[]) {
super(properties, children);

const url = new URL(location.href, document.location.origin),
pattern = (this.constructor as typeof Page).route.split('/'),
pathname = url.pathname.split('/');
this.urlParams = Object.fromEntries(url.searchParams.entries());

zipWith(pattern, pathname, (p, n) => {
if (p.startsWith(':'))
this.urlParams[p.slice(1)] = n;
});

const sheet = new CSSStyleSheet(),
pageStyle = this._initPageStyle(),
styleElement = document.createElement('style');
Expand Down
27 changes: 0 additions & 27 deletions src/presentation/pages/SlugPage.mts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { MiniCards, MiniCard } from '~/presentation/components/index.mjs';
import Page from '../Page.mjs';

export class Environment extends Page {
export default class EnvironmentPage extends Page {
static override route = '/environments/:slug';
static {
customElements.define('x-environment-page', this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import EnvironmentRepository from '~/data/EnvironmentRepository.mjs';

const { p } = html;

export class Environments extends Page {
export default class EnvironmentsPage extends Page {
static override route = '/environments';
static {
customElements.define('x-environments-page', this);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import html from '~/presentation/lib/html.mjs';
import { DataTable } from '~/presentation/components/DataTable.mjs';
import SlugPage from '../SlugPage.mjs';
import GlossaryTerm from '~/domain/GlossaryTerm.mjs';
import EnvironmentRepository from '~/data/EnvironmentRepository.mjs';
import GlossaryRepository from '~/data/GlossaryRepository.mjs';
import type Environment from '~/domain/Environment.mjs';
import Page from '../Page.mjs';

const { p } = html;

export class Glossary extends SlugPage {
export default class GlossaryPage extends Page {
static override route = '/environments/:slug/glossary';
static {
customElements.define('x-glossary-page', this);
}
Expand Down Expand Up @@ -59,7 +60,7 @@ export class Glossary extends SlugPage {

this.#environmentRepository.addEventListener('update', () => dataTable.renderData());
this.#glossaryRepository.addEventListener('update', () => dataTable.renderData());
this.#environmentRepository.getBySlug(this.slug).then(environment => {
this.#environmentRepository.getBySlug(this.urlParams['slug']).then(environment => {
this.#environment = environment;
dataTable.renderData();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import requiredTheme from '~/presentation/theme/requiredTheme.mjs';

const { form, label, input, span, button } = html;

export class NewEnvironment extends Page {
export default class NewEnvironmentPage extends Page {
static override route = '/environments/new-entry';
static {
customElements.define('x-new-environment-page', this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import GoalsRepository from '~/data/GoalsRepository.mjs';
import BehaviorRepository from '~/data/BehaviorRepository.mjs';
import html from '~/presentation/lib/html.mjs';
import { DataTable } from '~/presentation/components/DataTable.mjs';
import SlugPage from '../SlugPage.mjs';
import Page from '../Page.mjs';

const { p, strong } = html;

export class Functionality extends SlugPage {
export default class FunctionalityPage extends Page {
static override route = '/goals/:slug/functionality';
static {
customElements.define('x-functionality-page', this);
}
Expand Down Expand Up @@ -58,7 +59,7 @@ export class Functionality extends SlugPage {

this.#goalsRepository.addEventListener('update', () => dataTable.renderData());
this.#behaviorRepository.addEventListener('update', () => dataTable.renderData());
this.#goalsRepository.getBySlug(this.slug).then(goals => {
this.#goalsRepository.getBySlug(this.urlParams['slug']).then(goals => {
this.#goals = goals;
dataTable.renderData();
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import Page from '../Page.mjs';
import { MiniCards, MiniCard } from '~/presentation/components/index.mjs';

export class Goal extends Page {
export default class GoalPage extends Page {
static override route = '/goals/:slug';
static {
customElements.define('x-goal-page', this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import GoalsRepository from '~/data/GoalsRepository.mjs';

const { p } = html;

export class Goals extends Page {
export default class GoalsPage extends Page {
static override route = '/goals';
static {
customElements.define('x-goals-page', this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import GoalsRepository from '~/data/GoalsRepository.mjs';
import LimitRepository from '~/data/LimitRepository.mjs';
import html from '~/presentation/lib/html.mjs';
import { DataTable } from '~/presentation/components/DataTable.mjs';
import SlugPage from '../SlugPage.mjs';
import Page from '../Page.mjs';

const { p } = html;

export class Limitations extends SlugPage {
export default class LimitationsPage extends Page {
static override route = '/goals/:slug/limitations';
static {
customElements.define('x-limitations-page', this);
}
Expand Down Expand Up @@ -60,7 +61,7 @@ export class Limitations extends SlugPage {

this.#goalsRepository.addEventListener('update', () => dataTable.renderData());
this.#limitRepository.addEventListener('update', () => dataTable.renderData());
this.#goalsRepository.getBySlug(this.slug).then(goals => {
this.#goalsRepository.getBySlug(this.urlParams['slug']).then(goals => {
this.#goals = goals;
dataTable.renderData();
});
Expand Down
Loading