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

routerdata 🔥 #81

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,6 @@ firebase.json

# vercel build output api
# https://vercel.com/docs/build-output-api/v3
.vercel
.vercel

pnpm-lock.yaml
40 changes: 39 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Status: Meme

A 2kB zero-config router and prefetcher that makes a static site feel like a blazingly fast SPA.
A 3kB zero-config router and prefetcher that makes a static site feel like a blazingly fast SPA.

## Why?

Expand Down Expand Up @@ -49,6 +49,10 @@ window.addEventListener('flamethrower:router:end', hideLoader);

// Disable it
router.enabled = false;

// Subscribe/unsubscribe specific routes to function
router.subscribe(['<route>'], <function name>);
router.unsubscribe('<route>', <function name>);
```

Opt-out of specific links for full page load.
Expand Down Expand Up @@ -85,6 +89,40 @@ Prefecthing is disabled by default.
const router = flamethrower({ prefetch: 'visible' });
```

### route subscriptions

the router class extends the routerdata class, which has the subscribe method

see example/main.js for some more complex use cases

```js
router.subscribe(['/logme/', '/'], <function name>);
router.unsubscribe('/', <function name>);

function logMe() {
console.log('hi mom');
}

// Subscribe all routes to function
router.subscribe(['*'], logMe);

// only call function once
// ie to create a closure over multiple subscriptions
router.subscribe(['**'], closeMe);
function closeMe() {
router.subscribe(['/false/'], falseMe);
router.subscribe(['/true/'], trueMe);
let val = true;
function falseMe() {
val = false;
}
function trueMe() {
val = true;
}
}
```


### Misc

**Supported in all browsers?** Yes. It will fallback to standard navigation if `window.history` does not exist.
Expand Down
13 changes: 10 additions & 3 deletions example/about/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="../favicon.ico" type="image/png" />
<link rel="icon" href="/favicon.ico" type="image/png" />
<meta name="description" content="The About Page" />
<meta name="extra" content="test" />
<title>About</title>
<!--
<script defer type="module">
import flamethrower from '/flamethrower.js';
flamethrower({ prefetch: 'visible', log: true, pageTransitions: true });
const router = flamethrower({ prefetch: 'visible', log: true, pageTransitions: true });
</script>
-->

<script defer type="module" src="/main.js"></script>
<script defer data-reload src="/script1.js"></script>
<script defer src="/script2.js"></script>

Expand Down Expand Up @@ -40,7 +43,7 @@ <h1 id="heading">About</h1>
<div id="keep" flamethrower-preserve>
<article>
<p>This text should be preserved when starting from about</p>
</article>
</article>
</div>


Expand All @@ -57,6 +60,10 @@ <h3>Scripts</h3>
<p>Scripts from body or head with data-reload attr should always run</p>
<p id="bodyCheck"></p>
<p id="headCheck"></p>
<p id="subscriberCheck"></p>
<p class="multi-route"></p>
<p id="subscriberCount"></p>
<p id="global-check"></p>
<p id="headCheck2">default text</p>

<div style="margin-top: 1000px"></div>
Expand Down
27 changes: 27 additions & 0 deletions example/bar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

export class Bar {
constructor(val) {
this._value = val;
}

logVal() {
console.log(this.value);
}

setText() {
let el = document.getElementById("global-check");
el.innerText = this.value;
}

get value() {
return this._value;
}

set value(val) {
if (this._value !== val) {
this._value = val;
this.logVal();
this.setText();
}
}
}
11 changes: 11 additions & 0 deletions example/foo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

export class Foo {
constructor(id, value) {
this.el = document.getElementById(id);
this.val = value;
}

apply() {
this.el.innerText = this.val;
}
}
10 changes: 8 additions & 2 deletions example/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
<link rel="icon" href="favicon.ico" type="image/png" />
<meta name="description" content="The Home Page" />
<title>Flamethrower</title>
<!--
<script defer type="module">
import flamethrower from '/flamethrower.js';
flamethrower({ prefetch: 'visible', log: true, pageTransitions: true });
const router = flamethrower({ prefetch: 'visible', log: true, pageTransitions: true });
</script>
-->
<script defer type="module" src="/main.js"></script>

<style>
#heading {
Expand All @@ -34,9 +37,12 @@ <h1 id="heading">Home</h1>
<div id="keep" flamethrower-preserve>
<article>
<p>This text should be preserved when starting from home</p>
</article>
</article>
</div>

<p id="unsub-check"></p>
<p id="global-check"></p>

<script>
// test
window.addEventListener('flamethrower:router:fetch-progress', ({ detail }) => {
Expand Down
65 changes: 65 additions & 0 deletions example/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import flamethrower from './flamethrower.js';

import { Bar } from './bar.js';

const router = flamethrower({ prefetch: 'visible', log: true, pageTransitions: true });

// subscribe all routes to function
router.subscribe(["*"], all);

// subscribe only one route
router.subscribe(["/about/"], oneRoute);
router.subscribe(["/"], unsubme);

// subscribe multiple routes to one function
router.subscribe(["/test/", "/about/"], multiroute);

// subscribe route to an unsubscription
router.subscribe(["/about/"], subUnsub);

// subscribe a closurish type deal
// probably not recommended to use this in most cases, unless working with global variables
router.subscribe(["**"], closeMe);

function all() {
console.log("look mom, you can call functions on every route!");
}

// dynamic import for route
async function oneRoute() {
const { Foo } = await import("./foo.js");
const foo = new Foo("subscriberCheck", "✔️ single route subscription works");
foo.apply();
}

async function unsubme() {
const { Foo } = await import("./foo.js");
const foo = new Foo("unsub-check", "✔️ go to about, come back, and I'll be gone. Don't worry, I'll come back on reload");
foo.apply();
}

function multiroute() {
let el = document.querySelector(".multi-route");
el.innerText = "✔️ multi route subscription works"
}

function subUnsub() {
router.unsubscribe("/", unsubme);
}

function closeMe() {
const bar = new Bar("hi");
router.subscribe(["/"], t1);
router.subscribe(["/about/"], t2);
router.subscribe(["/test/"], t3);
function t1() {
bar.value = "✔️ hi mom";
}
function t2() {
bar.value = "✔️ hi son";
}
function t3() {
bar.value = "✔️ how's my little computer scientist today?";
}
}

7 changes: 6 additions & 1 deletion example/test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="../favicon.ico" type="image/png" />
<title>Test Page</title>
<!--
<script defer type="module">
import flamethrower from '/flamethrower.js';
flamethrower({ prefetch: 'visible', log: true, pageTransitions: true });
const router = flamethrower({ prefetch: 'visible', log: true, pageTransitions: true });
</script>
-->
<script defer type="module" src="/main.js"></script>
<style>
#load-bar {
background-color: blue;
Expand All @@ -26,6 +29,8 @@
<h1 id="heading">Test Page</h1>
<a href="/">home</a>
<a href="/about">about</a>
<p class="multi-route"></p>
<p id="global-check"></p>
<script>
// test
window.addEventListener('flamethrower:router:fetch-progress', ({ detail }) => {
Expand Down
4 changes: 4 additions & 0 deletions lib/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { Router } from './router';

export interface Subscription {
(): void | Promise<void>;
}

export interface FlamethrowerOptions {
log?: boolean;
/**
Expand Down
33 changes: 33 additions & 0 deletions lib/pool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Subscription } from './interfaces';

export class Pool {
public insert: number;
public buffer: Subscription[];
constructor() {
this.insert = -1;
this.buffer = [];
}

/**
* @param {Subscription} val
* @returns {number}
* adds the callback function to the buffer
* if it does not yet contain it
*/
public push(val: Subscription): number {
if (!this.buffer.includes(val)) {
this.insert++;
this.buffer[this.insert] = val;
}
// return the index of the specific function
return this.buffer.indexOf(val);
}

/**
* @param {number} idx
*/
public del(idx: number) {
this.buffer.splice(idx, 1);
this.insert--;
}
}
5 changes: 4 additions & 1 deletion lib/router.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { RouterData } from './routerdata';
import { FetchProgressEvent, FlamethrowerOptions, RouteChangeData } from './interfaces';
import { addToPushState, handleLinkClick, handlePopState, scrollTo } from './handlers';
import { mergeHead, formatNextDocument, replaceBody, runScripts } from './dom';
Expand All @@ -7,12 +8,13 @@ const defaultOpts = {
pageTransitions: false,
};

export class Router {
export class Router extends RouterData {
public enabled = true;
private prefetched = new Set<string>();
private observer: IntersectionObserver;

constructor(public opts?: FlamethrowerOptions) {
super();
this.opts = { ...defaultOpts, ...(opts ?? {}) };

if (window?.history) {
Expand Down Expand Up @@ -244,6 +246,7 @@ export class Router {
scrollTo(type, scrollId);
}

this.notify();

window.dispatchEvent(new CustomEvent('flamethrower:router:end'));

Expand Down
Loading