stx-router 0.2.338 → 0.2.340
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.js +98 -7
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -55,7 +55,7 @@ function routerSource() {
|
|
|
55
55
|
if(window.__stxRouter&&window.__stxRouter.__rev===ROUTER_REV)return;
|
|
56
56
|
|
|
57
57
|
// \u2500\u2500 Configuration \u2500\u2500
|
|
58
|
-
var defaults={container:'main',loadingClass:'stx-navigating',viewTransitions:true,cache:true,scrollToTop:true,prefetch:true,progress:true,progressColor:'#78dce8',progressHeight:'2px',interceptAllLinks:false,prefetchCacheMax:50,routeFocus:true,announceRoute:true,interceptForms:false,cssLoadTimeout:1500};
|
|
58
|
+
var defaults={container:'main',loadingClass:'stx-navigating',viewTransitions:true,cache:true,scrollToTop:true,prefetch:true,progress:true,progressColor:'#78dce8',progressHeight:'2px',interceptAllLinks:false,prefetchCacheMax:50,routeFocus:true,announceRoute:true,interceptForms:false,cssLoadTimeout:1500,scrollRestoration:true};
|
|
59
59
|
var o=Object.assign({},defaults,window.__stxRouterConfig||{},window.STX_ROUTER_OPTIONS||{});
|
|
60
60
|
var containerSel=o.container;
|
|
61
61
|
var debug=!!o.debug;
|
|
@@ -72,6 +72,84 @@ function routerSource() {
|
|
|
72
72
|
function ce(tag){return document.createElement(tag)}
|
|
73
73
|
function dhead(){return document.head}
|
|
74
74
|
|
|
75
|
+
// \u2500\u2500 Scroll position \u2500\u2500
|
|
76
|
+
// Forward navigation goes to the top, or to the hash. Back and forward return
|
|
77
|
+
// to where the entry was left -- the browser cannot do that for us, because
|
|
78
|
+
// by the time it would restore, the content of the entry has not been fetched
|
|
79
|
+
// yet, so it clamps to whatever height the outgoing page happened to have.
|
|
80
|
+
// history.scrollRestoration therefore goes to manual and the positions are
|
|
81
|
+
// kept here, keyed by a token written into each history entry (two entries
|
|
82
|
+
// for the same URL keep their own positions, which is the point).
|
|
83
|
+
//
|
|
84
|
+
// Manual restoration also takes the browser's reload behaviour away, so the
|
|
85
|
+
// positions are mirrored into sessionStorage: a reload, or a return to the
|
|
86
|
+
// tab, finds the entry's token already in history.state and restores from
|
|
87
|
+
// there. sessionStorage is per tab and dies with it, same as the history.
|
|
88
|
+
var SCROLL_TOKEN='__stxScroll';
|
|
89
|
+
var SCROLL_STORE='stx:scroll:';
|
|
90
|
+
var scrollSeq=0;
|
|
91
|
+
var pendingScroll=null;
|
|
92
|
+
var restoreScroll=!!o.scrollRestoration;
|
|
93
|
+
function newScrollToken(){return 's'+(++scrollSeq)+'-'+Date.now()}
|
|
94
|
+
var scrollToken=(history.state&&history.state[SCROLL_TOKEN])||newScrollToken();
|
|
95
|
+
// sessionStorage is the only store: it is synchronous, it is per tab, it dies
|
|
96
|
+
// with the tab exactly as the history does, and it is the one that survives a
|
|
97
|
+
// reload. A private window that refuses it degrades to scrolling to the top.
|
|
98
|
+
function readScroll(token){
|
|
99
|
+
try{
|
|
100
|
+
var parts=(window.sessionStorage.getItem(SCROLL_STORE+token)||'').split(',');
|
|
101
|
+
return parts.length===2?[parseFloat(parts[0])||0,parseFloat(parts[1])||0]:null;
|
|
102
|
+
}catch(e){return null}
|
|
103
|
+
}
|
|
104
|
+
// Called while the outgoing entry is still the current one, so the position
|
|
105
|
+
// read here belongs to the token being written.
|
|
106
|
+
function rememberScroll(){
|
|
107
|
+
if(!restoreScroll)return;
|
|
108
|
+
try{window.sessionStorage.setItem(SCROLL_STORE+scrollToken,(window.pageXOffset||window.scrollX||0)+','+(window.pageYOffset||window.scrollY||0))}catch(e){}
|
|
109
|
+
}
|
|
110
|
+
// One place decides where a swap leaves the viewport, so the fragment path
|
|
111
|
+
// and the whole-document path cannot drift apart.
|
|
112
|
+
function applyScroll(hash){
|
|
113
|
+
if(pendingScroll){
|
|
114
|
+
var at=pendingScroll;
|
|
115
|
+
pendingScroll=null;
|
|
116
|
+
window.scrollTo({left:at[0],top:at[1],behavior:'instant'});
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if(o.scrollToTop&&!hash){window.scrollTo({top:0,behavior:'instant'});return}
|
|
120
|
+
if(hash){var el=qs(hash);if(el)el.scrollIntoView({behavior:'smooth'})}
|
|
121
|
+
}
|
|
122
|
+
// Keeps whatever another script put in the entry; only adds the token.
|
|
123
|
+
function stampScrollToken(){
|
|
124
|
+
var state={};
|
|
125
|
+
var current=history.state;
|
|
126
|
+
if(current&&typeof current==='object')for(var key in current)state[key]=current[key];
|
|
127
|
+
state[SCROLL_TOKEN]=scrollToken;
|
|
128
|
+
try{history.replaceState(state,'',location.href)}catch(e){}
|
|
129
|
+
}
|
|
130
|
+
if(restoreScroll){
|
|
131
|
+
try{history.scrollRestoration='manual'}catch(e){}
|
|
132
|
+
// A token already in the entry means this document is a reload or a
|
|
133
|
+
// restore of an entry visited before, so its position is worth asking for.
|
|
134
|
+
var known=history.state&&history.state[SCROLL_TOKEN];
|
|
135
|
+
if(known){
|
|
136
|
+
var saved=readScroll(scrollToken);
|
|
137
|
+
if(saved&&(saved[0]||saved[1])){
|
|
138
|
+
var restoreAt=function(){window.scrollTo({left:saved[0],top:saved[1],behavior:'instant'})};
|
|
139
|
+
if(document.readyState==='complete')restoreAt();
|
|
140
|
+
else window.addEventListener('load',restoreAt,{once:true});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
else{
|
|
144
|
+
stampScrollToken();
|
|
145
|
+
}
|
|
146
|
+
// A reload or a link out of the app never calls writeHistory, so this is
|
|
147
|
+
// the only chance to record where the page was left. pagehide fires for
|
|
148
|
+
// both, and unlike beforeunload it does not keep the page out of the
|
|
149
|
+
// back/forward cache.
|
|
150
|
+
window.addEventListener('pagehide',rememberScroll);
|
|
151
|
+
}
|
|
152
|
+
|
|
75
153
|
// Replace, never merge: absent payloads must forget the outgoing page's data.
|
|
76
154
|
// Run at the committed swap, not during prefetch or a superseded navigation.
|
|
77
155
|
function hydrateServerData(html){
|
|
@@ -662,9 +740,14 @@ function routerSource() {
|
|
|
662
740
|
// through swap() to each of the three history sites would have been the
|
|
663
741
|
// alternative (#1807).
|
|
664
742
|
function writeHistory(mode,href){
|
|
665
|
-
|
|
666
|
-
|
|
743
|
+
// The position belongs to the entry being left, and scrollToken still
|
|
744
|
+
// names it here.
|
|
745
|
+
rememberScroll();
|
|
746
|
+
if(mode==='replace'){history.replaceState(tokenState(),'',href);return}
|
|
747
|
+
scrollToken=newScrollToken();
|
|
748
|
+
history.pushState(tokenState(),'',href);
|
|
667
749
|
}
|
|
750
|
+
function tokenState(){var state={};state[SCROLL_TOKEN]=scrollToken;return state}
|
|
668
751
|
|
|
669
752
|
// Second arg accepts the legacy pushState boolean OR an options object
|
|
670
753
|
// { replace }. Callers inside this file still pass the boolean.
|
|
@@ -1119,8 +1202,7 @@ else {
|
|
|
1119
1202
|
qsa('script[data-stx-page]').forEach(function(s){s.remove()});
|
|
1120
1203
|
if(pushState!==false)writeHistory(pushState,url+(hash||''));
|
|
1121
1204
|
refreshCurrentLinks();
|
|
1122
|
-
|
|
1123
|
-
else if(hash){var el=qs(hash);if(el)el.scrollIntoView({behavior:'smooth'})}
|
|
1205
|
+
applyScroll(hash);
|
|
1124
1206
|
window.dispatchEvent(new CustomEvent('stx:navigate',{detail:{url:url}}));
|
|
1125
1207
|
// Before page scripts run, so a page that focuses its own control on
|
|
1126
1208
|
// mount still wins \u2014 its script executes after this.
|
|
@@ -1578,8 +1660,7 @@ else {
|
|
|
1578
1660
|
refreshCurrentLinks();
|
|
1579
1661
|
|
|
1580
1662
|
// Scroll
|
|
1581
|
-
|
|
1582
|
-
else if(hash){var el=qs(hash);if(el)el.scrollIntoView({behavior:'smooth'})}
|
|
1663
|
+
applyScroll(hash);
|
|
1583
1664
|
|
|
1584
1665
|
// Update title
|
|
1585
1666
|
var newTitle=doc.querySelector('title');
|
|
@@ -1945,6 +2026,16 @@ else {
|
|
|
1945
2026
|
|
|
1946
2027
|
// \u2500\u2500 Back/forward \u2500\u2500
|
|
1947
2028
|
window.addEventListener('popstate',function(){
|
|
2029
|
+
// Nothing has scrolled yet (restoration is manual), so the viewport still
|
|
2030
|
+
// shows the entry being left: save it under its own token before adopting
|
|
2031
|
+
// the popped entry's, and hand the swap the position to land on.
|
|
2032
|
+
rememberScroll();
|
|
2033
|
+
if(restoreScroll){
|
|
2034
|
+
var popped=history.state&&history.state[SCROLL_TOKEN];
|
|
2035
|
+
scrollToken=popped||newScrollToken();
|
|
2036
|
+
if(!popped)stampScrollToken();
|
|
2037
|
+
pendingScroll=readScroll(scrollToken);
|
|
2038
|
+
}
|
|
1948
2039
|
navigate(location.pathname+location.search+location.hash,false);
|
|
1949
2040
|
});
|
|
1950
2041
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stx-router",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.340",
|
|
5
5
|
"description": "File-based router for STX — .stx template discovery, nested layouts, typed route params, middleware, and client-side SPA navigation.",
|
|
6
6
|
"author": "Chris Breuer <chris@stacksjs.org>",
|
|
7
7
|
"license": "MIT",
|