Comparing version 2.0.4 to 3.0.0
import { smitter } from "smitter"; | ||
import { qs, on } from "martha"; | ||
function create({ transitions }) { | ||
let IDLE = "idle"; | ||
let LEAVING = "leaving"; | ||
let ENTERING = "entering"; | ||
let emitter = smitter(); | ||
let lastHref = null; | ||
let cache = { | ||
@@ -22,7 +18,3 @@ [window.location.pathname]: document.documentElement.outerHTML | ||
let trigger = "load"; | ||
let abortController = new AbortController(); | ||
let parser = new DOMParser(); | ||
let status = IDLE; | ||
let leaveCancelled = false; | ||
let enterCancelled = false; | ||
on(document, "click", (event) => { | ||
@@ -35,6 +27,9 @@ var _a; | ||
} | ||
let el = target == null ? void 0 : target.closest('a[href]:not([target]):not([href|="#"]):not([a-ignore])'); | ||
let el = target == null ? void 0 : target.closest('a[href]:not([target]):not([href*="#"]):not([a-ignore])'); | ||
if (el) { | ||
let href = el.getAttribute("href"); | ||
if (href == null ? void 0 : href.length) { | ||
if (href.toLowerCase().startsWith("http") && new URL(href).hostname.replace("www.", "") !== window.location.hostname) { | ||
return; | ||
} | ||
let url = new URL(href, window.location.origin); | ||
@@ -78,8 +73,2 @@ let transition = (_a = el.getAttribute("a-transition")) != null ? _a : "default"; | ||
let { leave, enter } = transitions[transition]; | ||
if (typeof leave !== "function") { | ||
throw new Error(`leave missing from: ${transition}`); | ||
} | ||
if (typeof enter !== "function") { | ||
throw new Error(`enter missing from: ${transition}`); | ||
} | ||
let html = null; | ||
@@ -90,16 +79,2 @@ from = qs("[a-page]", root); | ||
} | ||
if (status === LEAVING) { | ||
leaveCancelled = true; | ||
abortController.abort(); | ||
emitter.emit("leaveCancelled", { href, from, trigger }); | ||
if (lastHref === href) { | ||
interruptLeaveWithEnter(href, popping, transition); | ||
return; | ||
} | ||
} | ||
if (status === ENTERING) { | ||
enterCancelled = true; | ||
emitter.emit("enterCancelled", { href, from, to, trigger }); | ||
} | ||
status = LEAVING; | ||
emitter.emit("beforeLeave", { href, from, trigger }); | ||
@@ -110,10 +85,5 @@ if (!popping) { | ||
html = (await Promise.all([get(href), leave({ from, href, trigger })]))[0]; | ||
if (leaveCancelled) { | ||
leaveCancelled = false; | ||
return; | ||
} | ||
if (!html) | ||
return; | ||
emitter.emit("afterLeave", { href, from, trigger }); | ||
status = ENTERING; | ||
let doc = parser.parseFromString(html, "text/html"); | ||
@@ -135,46 +105,11 @@ let tmpRoot = qs("[a-root]", doc); | ||
await enter({ from, to, trigger }); | ||
if (enterCancelled) { | ||
enterCancelled = false; | ||
return; | ||
} | ||
emitter.emit("afterEnter", { href, from, to, doc, trigger }); | ||
status = IDLE; | ||
lastHref = href; | ||
} | ||
async function interruptLeaveWithEnter(href, popping, transition) { | ||
if (status === ENTERING) { | ||
enterCancelled = true; | ||
emitter.emit("enterCancelled"); | ||
} | ||
status = ENTERING; | ||
let to2 = qs("[a-page]", root); | ||
if (!to2) { | ||
throw new Error("[a-page] element missing"); | ||
} | ||
if (!popping) { | ||
window.history.pushState(null, "", href); | ||
} | ||
emitter.emit("beforeEnter", { href, to: to2 }); | ||
await transitions[transition].enter({ to: to2, leaveCancelled: true, trigger }); | ||
if (enterCancelled) { | ||
enterCancelled = false; | ||
return; | ||
} | ||
emitter.emit("afterEnter", { href, to: to2, trigger }); | ||
status = IDLE; | ||
} | ||
async function get(href) { | ||
abortController = new AbortController(); | ||
let html = cache[href]; | ||
if (html) | ||
return html; | ||
try { | ||
html = await fetch(href, { | ||
credentials: "include", | ||
signal: abortController.signal | ||
}).then((res) => res.text()); | ||
} catch (error) { | ||
if ((error == null ? void 0 : error.name) === "AbortError") | ||
return; | ||
} | ||
html = await fetch(href, { | ||
credentials: "include" | ||
}).then((res) => res.text()); | ||
cache[href] = html; | ||
@@ -181,0 +116,0 @@ return html; |
@@ -1,1 +0,1 @@ | ||
var alio=function(g,S,a){"use strict";function K({transitions:h}){let v="idle",A="leaving",p="entering",l=S.smitter(),L=null,P={[window.location.pathname]:document.documentElement.outerHTML},w=a.qs("[a-root]");if(!w)throw new Error("[a-root] element missing");let o=a.qs("[a-page]",w);if(!o)throw new Error("[a-page] element missing");let m=null,r="load",y=new AbortController,M=new DOMParser,f=v,b=!1,c=!1;return a.on(document,"click",e=>{var d;let t=e,n=t.target;if(t.ctrlKey||t.metaKey||t.altKey||t.shiftKey||t.defaultPrevented)return;let i=n==null?void 0:n.closest('a[href]:not([target]):not([href|="#"]):not([a-ignore])');if(i){let u=i.getAttribute("href");if(u==null?void 0:u.length){let s=new URL(u,window.location.origin),E=(d=i.getAttribute("a-transition"))!=null?d:"default";s.pathname!==window.location.pathname?(r=i,C(s.href,!1,E)):l.emit("samePage"),t.preventDefault()}}}),a.on(window,"popstate",()=>{r="popstate",C(window.location.href,!0)}),requestAnimationFrame(()=>{l.emit("beforeEnter",{href:window.location.href,to:o,doc:document,trigger:r}),h.default.enter({to:o,trigger:r}).then(()=>{l.emit("afterEnter",{href:window.location.href,to:o,doc:document,trigger:r})})}),{on:l.on,go:e=>C(e)};async function C(e,t=!1,n="default"){let{leave:i,enter:d}=h[n];if(typeof i!="function")throw new Error(`leave missing from: ${n}`);if(typeof d!="function")throw new Error(`enter missing from: ${n}`);let u=null;if(o=a.qs("[a-page]",w),!o)throw new Error("[a-page] element missing");if(f===A&&(b=!0,y.abort(),l.emit("leaveCancelled",{href:e,from:o,trigger:r}),L===e)){D(e,t,n);return}if(f===p&&(c=!0,l.emit("enterCancelled",{href:e,from:o,to:m,trigger:r})),f=A,l.emit("beforeLeave",{href:e,from:o,trigger:r}),t||window.history.pushState(null,"",e),u=(await Promise.all([I(e),i({from:o,href:e,trigger:r})]))[0],b){b=!1;return}if(!u)return;l.emit("afterLeave",{href:e,from:o,trigger:r}),f=p;let s=M.parseFromString(u,"text/html"),E=a.qs("[a-root]",s);if(!E)throw new Error("[a-root] element missing from incoming html");if(m=a.qs("[a-page]",E),!m)throw new Error("[a-page] element missing from incoming html");let q=a.qs("title",s);if(q&&q.textContent&&(document.title=q.textContent),w.append(m),l.emit("beforeEnter",{href:e,from:o,to:m,doc:s,trigger:r}),await d({from:o,to:m,trigger:r}),c){c=!1;return}l.emit("afterEnter",{href:e,from:o,to:m,doc:s,trigger:r}),f=v,L=e}async function D(e,t,n){f===p&&(c=!0,l.emit("enterCancelled")),f=p;let i=a.qs("[a-page]",w);if(!i)throw new Error("[a-page] element missing");if(t||window.history.pushState(null,"",e),l.emit("beforeEnter",{href:e,to:i}),await h[n].enter({to:i,leaveCancelled:!0,trigger:r}),c){c=!1;return}l.emit("afterEnter",{href:e,to:i,trigger:r}),f=v}async function I(e){y=new AbortController;let t=P[e];if(t)return t;try{t=await fetch(e,{credentials:"include",signal:y.signal}).then(n=>n.text())}catch(n){if((n==null?void 0:n.name)==="AbortError")return}return P[e]=t,t}}return g.create=K,Object.defineProperty(g,"__esModule",{value:!0}),g[Symbol.toStringTag]="Module",g}({},smitter,martha); | ||
var alio=function(d,v,r){"use strict";function y({transitions:h}){let l=v.smitter(),E={[window.location.pathname]:document.documentElement.outerHTML},c=r.qs("[a-root]");if(!c)throw new Error("[a-root] element missing");let n=r.qs("[a-page]",c);if(!n)throw new Error("[a-page] element missing");let s=null,o="load",b=new DOMParser;return r.on(document,"click",e=>{var w;let t=e,a=t.target;if(t.ctrlKey||t.metaKey||t.altKey||t.shiftKey||t.defaultPrevented)return;let u=a==null?void 0:a.closest('a[href]:not([target]):not([href*="#"]):not([a-ignore])');if(u){let i=u.getAttribute("href");if(i!=null&&i.length){if(i.toLowerCase().startsWith("http")&&new URL(i).hostname.replace("www.","")!==window.location.hostname)return;let m=new URL(i,window.location.origin),f=(w=u.getAttribute("a-transition"))!=null?w:"default";m.pathname!==window.location.pathname?(o=u,g(m.href,!1,f)):l.emit("samePage"),t.preventDefault()}}}),r.on(window,"popstate",()=>{o="popstate",g(window.location.href,!0)}),requestAnimationFrame(()=>{l.emit("beforeEnter",{href:window.location.href,to:n,doc:document,trigger:o}),h.default.enter({to:n,trigger:o}).then(()=>{l.emit("afterEnter",{href:window.location.href,to:n,doc:document,trigger:o})})}),{on:l.on,go:e=>g(e)};async function g(e,t=!1,a="default"){let{leave:u,enter:w}=h[a],i=null;if(n=r.qs("[a-page]",c),!n)throw new Error("[a-page] element missing");if(l.emit("beforeLeave",{href:e,from:n,trigger:o}),t||window.history.pushState(null,"",e),i=(await Promise.all([q(e),u({from:n,href:e,trigger:o})]))[0],!i)return;l.emit("afterLeave",{href:e,from:n,trigger:o});let m=b.parseFromString(i,"text/html"),f=r.qs("[a-root]",m);if(!f)throw new Error("[a-root] element missing from incoming html");if(s=r.qs("[a-page]",f),!s)throw new Error("[a-page] element missing from incoming html");let p=r.qs("title",m);p&&p.textContent&&(document.title=p.textContent),c.append(s),l.emit("beforeEnter",{href:e,from:n,to:s,doc:m,trigger:o}),await w({from:n,to:s,trigger:o}),l.emit("afterEnter",{href:e,from:n,to:s,doc:m,trigger:o})}async function q(e){let t=E[e];return t||(t=await fetch(e,{credentials:"include"}).then(a=>a.text()),E[e]=t,t)}}return d.create=y,Object.defineProperties(d,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}}),d}({},smitter,martha); |
@@ -1,1 +0,1 @@ | ||
(function(a,w){typeof exports=="object"&&typeof module!="undefined"?w(exports,require("smitter"),require("martha")):typeof define=="function"&&define.amd?define(["exports","smitter","martha"],w):(a=typeof globalThis!="undefined"?globalThis:a||self,w(a.alio={},a.smitter,a.martha))})(this,function(a,w,f){"use strict";function S({transitions:y}){let v="idle",L="leaving",E="entering",i=w.smitter(),x=null,P={[window.location.pathname]:document.documentElement.outerHTML},g=f.qs("[a-root]");if(!g)throw new Error("[a-root] element missing");let o=f.qs("[a-page]",g);if(!o)throw new Error("[a-page] element missing");let m=null,r="load",b=new AbortController,T=new DOMParser,s=v,C=!1,d=!1;return f.on(document,"click",e=>{var p;let t=e,n=t.target;if(t.ctrlKey||t.metaKey||t.altKey||t.shiftKey||t.defaultPrevented)return;let l=n==null?void 0:n.closest('a[href]:not([target]):not([href|="#"]):not([a-ignore])');if(l){let u=l.getAttribute("href");if(u==null?void 0:u.length){let c=new URL(u,window.location.origin),h=(p=l.getAttribute("a-transition"))!=null?p:"default";c.pathname!==window.location.pathname?(r=l,q(c.href,!1,h)):i.emit("samePage"),t.preventDefault()}}}),f.on(window,"popstate",()=>{r="popstate",q(window.location.href,!0)}),requestAnimationFrame(()=>{i.emit("beforeEnter",{href:window.location.href,to:o,doc:document,trigger:r}),y.default.enter({to:o,trigger:r}).then(()=>{i.emit("afterEnter",{href:window.location.href,to:o,doc:document,trigger:r})})}),{on:i.on,go:e=>q(e)};async function q(e,t=!1,n="default"){let{leave:l,enter:p}=y[n];if(typeof l!="function")throw new Error(`leave missing from: ${n}`);if(typeof p!="function")throw new Error(`enter missing from: ${n}`);let u=null;if(o=f.qs("[a-page]",g),!o)throw new Error("[a-page] element missing");if(s===L&&(C=!0,b.abort(),i.emit("leaveCancelled",{href:e,from:o,trigger:r}),x===e)){K(e,t,n);return}if(s===E&&(d=!0,i.emit("enterCancelled",{href:e,from:o,to:m,trigger:r})),s=L,i.emit("beforeLeave",{href:e,from:o,trigger:r}),t||window.history.pushState(null,"",e),u=(await Promise.all([M(e),l({from:o,href:e,trigger:r})]))[0],C){C=!1;return}if(!u)return;i.emit("afterLeave",{href:e,from:o,trigger:r}),s=E;let c=T.parseFromString(u,"text/html"),h=f.qs("[a-root]",c);if(!h)throw new Error("[a-root] element missing from incoming html");if(m=f.qs("[a-page]",h),!m)throw new Error("[a-page] element missing from incoming html");let A=f.qs("title",c);if(A&&A.textContent&&(document.title=A.textContent),g.append(m),i.emit("beforeEnter",{href:e,from:o,to:m,doc:c,trigger:r}),await p({from:o,to:m,trigger:r}),d){d=!1;return}i.emit("afterEnter",{href:e,from:o,to:m,doc:c,trigger:r}),s=v,x=e}async function K(e,t,n){s===E&&(d=!0,i.emit("enterCancelled")),s=E;let l=f.qs("[a-page]",g);if(!l)throw new Error("[a-page] element missing");if(t||window.history.pushState(null,"",e),i.emit("beforeEnter",{href:e,to:l}),await y[n].enter({to:l,leaveCancelled:!0,trigger:r}),d){d=!1;return}i.emit("afterEnter",{href:e,to:l,trigger:r}),s=v}async function M(e){b=new AbortController;let t=P[e];if(t)return t;try{t=await fetch(e,{credentials:"include",signal:b.signal}).then(n=>n.text())}catch(n){if((n==null?void 0:n.name)==="AbortError")return}return P[e]=t,t}}a.create=S,Object.defineProperty(a,"__esModule",{value:!0}),a[Symbol.toStringTag]="Module"}); | ||
(function(r,c){typeof exports=="object"&&typeof module!="undefined"?c(exports,require("smitter"),require("martha")):typeof define=="function"&&define.amd?define(["exports","smitter","martha"],c):(r=typeof globalThis!="undefined"?globalThis:r||self,c(r.alio={},r.smitter,r.martha))})(this,function(r,c,a){"use strict";function q({transitions:y}){let l=c.smitter(),E={[window.location.pathname]:document.documentElement.outerHTML},d=a.qs("[a-root]");if(!d)throw new Error("[a-root] element missing");let n=a.qs("[a-page]",d);if(!n)throw new Error("[a-page] element missing");let f=null,o="load",v=new DOMParser;return a.on(document,"click",e=>{var w;let t=e,s=t.target;if(t.ctrlKey||t.metaKey||t.altKey||t.shiftKey||t.defaultPrevented)return;let u=s==null?void 0:s.closest('a[href]:not([target]):not([href*="#"]):not([a-ignore])');if(u){let i=u.getAttribute("href");if(i!=null&&i.length){if(i.toLowerCase().startsWith("http")&&new URL(i).hostname.replace("www.","")!==window.location.hostname)return;let m=new URL(i,window.location.origin),p=(w=u.getAttribute("a-transition"))!=null?w:"default";m.pathname!==window.location.pathname?(o=u,g(m.href,!1,p)):l.emit("samePage"),t.preventDefault()}}}),a.on(window,"popstate",()=>{o="popstate",g(window.location.href,!0)}),requestAnimationFrame(()=>{l.emit("beforeEnter",{href:window.location.href,to:n,doc:document,trigger:o}),y.default.enter({to:n,trigger:o}).then(()=>{l.emit("afterEnter",{href:window.location.href,to:n,doc:document,trigger:o})})}),{on:l.on,go:e=>g(e)};async function g(e,t=!1,s="default"){let{leave:u,enter:w}=y[s],i=null;if(n=a.qs("[a-page]",d),!n)throw new Error("[a-page] element missing");if(l.emit("beforeLeave",{href:e,from:n,trigger:o}),t||window.history.pushState(null,"",e),i=(await Promise.all([b(e),u({from:n,href:e,trigger:o})]))[0],!i)return;l.emit("afterLeave",{href:e,from:n,trigger:o});let m=v.parseFromString(i,"text/html"),p=a.qs("[a-root]",m);if(!p)throw new Error("[a-root] element missing from incoming html");if(f=a.qs("[a-page]",p),!f)throw new Error("[a-page] element missing from incoming html");let h=a.qs("title",m);h&&h.textContent&&(document.title=h.textContent),d.append(f),l.emit("beforeEnter",{href:e,from:n,to:f,doc:m,trigger:o}),await w({from:n,to:f,trigger:o}),l.emit("afterEnter",{href:e,from:n,to:f,doc:m,trigger:o})}async function b(e){let t=E[e];return t||(t=await fetch(e,{credentials:"include"}).then(s=>s.text()),E[e]=t,t)}}r.create=q,Object.defineProperties(r,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}); |
@@ -1,2 +0,2 @@ | ||
export declare type AlioTriggerElement = Element | 'popstate' | 'load'; | ||
export declare type AlioTrigger = Element | 'popstate' | 'load'; | ||
export interface AlioEnterOptions { | ||
@@ -6,4 +6,3 @@ to: Element; | ||
href?: string; | ||
leaveCancelled?: boolean; | ||
trigger: AlioTriggerElement; | ||
trigger: AlioTrigger; | ||
} | ||
@@ -13,5 +12,5 @@ export interface AlioLeaveOptions { | ||
href: string; | ||
trigger: AlioTriggerElement; | ||
trigger: AlioTrigger; | ||
} | ||
export declare type AlioEnter = ({ to, from, href, leaveCancelled, trigger, }: AlioEnterOptions) => PromiseLike<any>; | ||
export declare type AlioEnter = ({ to, from, href, trigger, }: AlioEnterOptions) => PromiseLike<any>; | ||
export declare type AlioLeave = ({ from, href }: AlioLeaveOptions) => PromiseLike<any>; | ||
@@ -29,2 +28,29 @@ export interface AlioTransition { | ||
export declare type AlioCache = Record<string, string>; | ||
export interface AlioEventMap { | ||
beforeEnter: { | ||
href: string; | ||
from?: Element; | ||
to: Element; | ||
doc?: Document; | ||
trigger?: AlioTrigger; | ||
}; | ||
afterEnter: { | ||
href: string; | ||
from?: Element; | ||
to: Element; | ||
doc?: Document; | ||
trigger: AlioTrigger; | ||
}; | ||
beforeLeave: { | ||
href: String; | ||
from: Element; | ||
trigger: AlioTrigger; | ||
}; | ||
afterLeave: { | ||
href: String; | ||
from: Element; | ||
trigger: AlioTrigger; | ||
}; | ||
samePage: undefined; | ||
} | ||
export interface AlioApi { | ||
@@ -31,0 +57,0 @@ on: (type: string, handler: (payload: any) => void) => any; |
{ | ||
"name": "alio", | ||
"version": "2.0.4", | ||
"version": "3.0.0", | ||
"description": "A lightweight pjax library", | ||
@@ -68,4 +68,4 @@ "files": [ | ||
"martha": "^4.0.3", | ||
"smitter": "^1.0.2" | ||
"smitter": "1.1.1" | ||
} | ||
} |
# alio | ||
A lightweight pjax library with a focus on animated page transitions. | ||
A lightweight pjax library for animated page transitions. | ||
## Features | ||
- 🦠 Tiny & minimal (1.3kb gizipped) | ||
- 🍴 Cancellable transitions | ||
- 🦠 Tiny & minimal (1kb gzipped) | ||
- 🗺️ Contextual transitions | ||
@@ -27,3 +26,3 @@ | ||
Then create an alio instance and a default transition. In alio, transitions are just objects with async `enter` and `leave` methods. In the example below, [`motion`](https://motion.dev) is used to implement a basic fade transition. | ||
Then create an alio instance and a transition called `default`. In alio, transitions are objects with async `enter` and `leave` methods. In the example below, [`motion`](https://motion.dev) is used to implement a basic fade transition, but feel free to use GSAP, or whatever other animation tool you'd like. | ||
@@ -39,3 +38,3 @@ ```js | ||
window.scroll(0, 0) | ||
from?.remove() | ||
from?.remove() // we leave it up to you to decide when to remove the previous page from the DOM | ||
await animate(to, { opacity: [0, 1] }).finished | ||
@@ -55,5 +54,2 @@ }, | ||
alio.on('afterEnter', ({ href, from, to }) => {}) | ||
alio.on('leaveCancelled', ({ href, from }) => {}) | ||
alio.on('enterCancelled', ({ href, from, to }) => {}) | ||
alio.on('error', (error) => {}) | ||
alio.on('samePage', () => {}) | ||
@@ -66,20 +62,1 @@ | ||
The alio instance offers an `on` method for listening to alio events. There is also a `go` method for programmatically redirecting to a different page. | ||
## TODO's | ||
- [ ] Complete API reference | ||
- [ ] Write tests | ||
- [ ] Set up CI/CD for releases | ||
- [ ] Build out examples | ||
- [x] Persistent navigation | ||
- [ ] GSAP | ||
- [ ] CSS | ||
- [ ] Overlapping transition | ||
- [ ] Contextual transition | ||
- [ ] Ignore | ||
- [ ] Analytics | ||
- [ ] Prefetching | ||
- [ ] Anchor Links | ||
13178
189
59
Updatedsmitter@1.1.1