@trullock/page-manager 0.14.4 → 1.1.0
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/package.json +2 -2
- package/readme.md +23 -14
- package/src/index.js +46 -92
- package/src/page.js +0 -2
- package/tests/lolpack.js +1 -1
- package/tests/page.js +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trullock/page-manager",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Single page app manager",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./src/index.js"
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@trullock/router": "^0.
|
|
20
|
+
"@trullock/router": "^1.0.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@babel/core": "^7.20.2",
|
package/readme.md
CHANGED
|
@@ -8,16 +8,20 @@ Create a basic page like this:
|
|
|
8
8
|
|
|
9
9
|
```
|
|
10
10
|
import {registerPage, Page} from '@trullock/page-manager';
|
|
11
|
-
registerPage(
|
|
12
|
-
|
|
11
|
+
registerPage({
|
|
12
|
+
name: 'view-thing',
|
|
13
|
+
route: '/things/{thingId}',
|
|
14
|
+
pageClass: class extends Page { }
|
|
13
15
|
});
|
|
14
16
|
```
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
`'view-thing'` is the name of the page, this can be used to build/look-up its url later without needing to hardcode url strings everywhere
|
|
19
|
+
|
|
20
|
+
`'/things/{thingId}'` is the url (route) of the page
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
`pageClass` is a class definition for the page.
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
This can by lazily loaded by providing `pageClassLoader: async () => { ... }` which returns the class type (not an instance of it)
|
|
21
25
|
|
|
22
26
|
### Routing
|
|
23
27
|
|
|
@@ -34,7 +38,11 @@ To build a URL from a route and some parameters, do this:
|
|
|
34
38
|
```
|
|
35
39
|
import { getPath, registerPage } from '@trullock/page-manager';
|
|
36
40
|
|
|
37
|
-
registerPage(
|
|
41
|
+
registerPage({
|
|
42
|
+
name: 'view-thing',
|
|
43
|
+
route: '/things/{thingId}',
|
|
44
|
+
pageClass: class extends Page { }
|
|
45
|
+
});
|
|
38
46
|
|
|
39
47
|
let url = getPath('view-thing', { thingId: 1 })
|
|
40
48
|
// url == '/things/1'
|
|
@@ -298,12 +306,12 @@ In the case that were was no goal (i.e. you went directly to the current page, i
|
|
|
298
306
|
PageManager is built so that it can lazily fetch page markup. To do this, use the following options:
|
|
299
307
|
|
|
300
308
|
```
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
return fetch(
|
|
309
|
+
registerPage({
|
|
310
|
+
name: 'view-thing',
|
|
311
|
+
route: '/things/{thingId}',
|
|
312
|
+
pageClass: class extends Page { },
|
|
313
|
+
loadMarkup: routeResult => {
|
|
314
|
+
return fetch('/pages/' + routeResult.name + '.html')
|
|
307
315
|
.then(r => r.text())
|
|
308
316
|
.then(html => {
|
|
309
317
|
var $div = document.createElement('div');
|
|
@@ -312,10 +320,11 @@ PageManager is built so that it can lazily fetch page markup. To do this, use th
|
|
|
312
320
|
return $div.firstElementChild;
|
|
313
321
|
})
|
|
314
322
|
.then($template => {
|
|
315
|
-
pageTemplateCache[
|
|
323
|
+
pageTemplateCache[routeResult.pattern] = $template;
|
|
316
324
|
return $template;
|
|
317
325
|
});
|
|
318
|
-
}
|
|
326
|
+
}
|
|
327
|
+
});
|
|
319
328
|
|
|
320
329
|
// Once a page has been fetched and rendered from its template, it needs attaching to the DOM. Use this options to control where it gets inserted.
|
|
321
330
|
attachMarkup: $html => document.body.appendChild($html),
|
package/src/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import router from '@trullock/router';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
pageCache = {},
|
|
3
|
+
var pageCache = {},
|
|
5
4
|
pageTemplateCache = {},
|
|
6
5
|
stack = [],
|
|
7
6
|
stackPointer = -1;
|
|
@@ -13,21 +12,7 @@ var lastNavigationDirection = null;
|
|
|
13
12
|
var goal = null;
|
|
14
13
|
var backData = {};
|
|
15
14
|
var options = {
|
|
16
|
-
|
|
17
|
-
fetchPageTemplate: async route => {
|
|
18
|
-
const response = await fetch(options.fetchPath(route));
|
|
19
|
-
const html = await response.text();
|
|
20
|
-
|
|
21
|
-
var $div = document.createElement('div');
|
|
22
|
-
$div.innerHTML = html;
|
|
23
|
-
const $template = $div.firstElementChild;
|
|
24
|
-
|
|
25
|
-
// TODO: why is this in here and not in the caller?
|
|
26
|
-
pageTemplateCache[route.pattern] = $template;
|
|
27
|
-
|
|
28
|
-
return $template;
|
|
29
|
-
},
|
|
30
|
-
pageInterrupt: route => null,
|
|
15
|
+
pageInterrupt: routeResult => null,
|
|
31
16
|
attachMarkup: $html => document.body.appendChild($html),
|
|
32
17
|
prepareMarkup: $html => { },
|
|
33
18
|
loadingPageName: 'loading',
|
|
@@ -36,35 +21,22 @@ var options = {
|
|
|
36
21
|
beforeHide: null
|
|
37
22
|
}
|
|
38
23
|
|
|
39
|
-
export
|
|
40
|
-
|
|
41
|
-
export function registerPage(argA, argB, argC) {
|
|
24
|
+
export function registerPage(opts) {
|
|
42
25
|
|
|
43
|
-
let
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
{
|
|
48
|
-
namedRoutes = argA;
|
|
49
|
-
pageClass = argB;
|
|
50
|
-
} else {
|
|
51
|
-
namedRoutes = {
|
|
52
|
-
[argA]: argB
|
|
53
|
-
};
|
|
54
|
-
pageClass = argC;
|
|
55
|
-
}
|
|
56
|
-
|
|
26
|
+
let loadPageClass = opts.pageClass ? (() => opts.pageClass) : opts.loadPageClass;
|
|
27
|
+
let namedRoutes = opts.namedRoutes || { [opts.name]: opts.route };
|
|
28
|
+
let auth = opts.auth || null;
|
|
29
|
+
let cacheMarkupBy = opts.cacheMarkupBy || 'path'
|
|
57
30
|
|
|
58
31
|
for (const [name, route] of Object.entries(namedRoutes)) {
|
|
59
|
-
router.addRoute(name, route,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
32
|
+
router.addRoute(name, route, {
|
|
33
|
+
route,
|
|
34
|
+
auth,
|
|
35
|
+
loadPageClass,
|
|
36
|
+
loadMarkup: opts.loadMarkup,
|
|
37
|
+
cacheMarkupBy
|
|
38
|
+
});
|
|
65
39
|
}
|
|
66
|
-
|
|
67
|
-
return pageClass;
|
|
68
40
|
}
|
|
69
41
|
|
|
70
42
|
export function getPath(name, values) {
|
|
@@ -92,14 +64,14 @@ function emitUrlChanged(url)
|
|
|
92
64
|
// TODO: 404 and error too?
|
|
93
65
|
function initLoading()
|
|
94
66
|
{
|
|
95
|
-
var
|
|
96
|
-
var route = router.parse(
|
|
67
|
+
var url = router.interpolate(options.loadingPageName, {});
|
|
68
|
+
var route = router.parse(url);
|
|
97
69
|
return loadPage(route, {});
|
|
98
70
|
}
|
|
99
71
|
|
|
100
72
|
function showLoading() {
|
|
101
|
-
var
|
|
102
|
-
var route = router.parse(
|
|
73
|
+
var url = router.interpolate(options.loadingPageName, {});
|
|
74
|
+
var route = router.parse(url);
|
|
103
75
|
var data = {
|
|
104
76
|
route: route,
|
|
105
77
|
scrollY: window.scrollY,
|
|
@@ -113,27 +85,28 @@ function showLoading() {
|
|
|
113
85
|
}
|
|
114
86
|
|
|
115
87
|
function hideLoading() {
|
|
116
|
-
var
|
|
117
|
-
var route = router.parse(
|
|
88
|
+
var url = router.interpolate(options.loadingPageName, {});
|
|
89
|
+
var route = router.parse(url);
|
|
118
90
|
var page = (pageCache[route.path] || pageCache[route.pattern]).page;
|
|
119
91
|
return Promise.resolve(page.hide());
|
|
120
92
|
}
|
|
121
93
|
|
|
122
|
-
async function loadPage(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
94
|
+
async function loadPage(routeResult, data)
|
|
95
|
+
{
|
|
96
|
+
if(!pageTemplateCache[routeResult.pattern])
|
|
97
|
+
pageTemplateCache[routeResult.pattern] = await routeResult.data.loadMarkup(routeResult);
|
|
98
|
+
let $template = pageTemplateCache[routeResult.pattern]
|
|
127
99
|
|
|
128
100
|
var $html = $template.cloneNode(true);
|
|
129
101
|
options.prepareMarkup($html);
|
|
130
102
|
options.attachMarkup($html);
|
|
131
103
|
|
|
132
|
-
|
|
104
|
+
routeResult.data.pageClass = await Promise.resolve(routeResult.data.loadPageClass());
|
|
105
|
+
let page = new (routeResult.data.pageClass)($html);
|
|
133
106
|
|
|
134
107
|
await Promise.resolve(page.boot(data));
|
|
135
108
|
|
|
136
|
-
let cacheKey =
|
|
109
|
+
let cacheKey = routeResult.data.cacheMarkupBy == 'path' ? routeResult.path : routeResult.pattern;
|
|
137
110
|
pageCache[cacheKey] = {
|
|
138
111
|
$html,
|
|
139
112
|
page
|
|
@@ -142,28 +115,29 @@ async function loadPage(route, data) {
|
|
|
142
115
|
return page;
|
|
143
116
|
}
|
|
144
117
|
|
|
145
|
-
async function showPage(url, data, event)
|
|
146
|
-
|
|
147
|
-
|
|
118
|
+
async function showPage(url, data, event)
|
|
119
|
+
{
|
|
120
|
+
var routeResult = router.parse(url);
|
|
121
|
+
if (routeResult == null) {
|
|
148
122
|
console.error(`Can't find page: '${url}'`);
|
|
149
123
|
|
|
150
|
-
let
|
|
151
|
-
|
|
124
|
+
let url = router.interpolate(options.error404PageName, {});
|
|
125
|
+
routeResult = router.parse(url)
|
|
152
126
|
}
|
|
153
127
|
|
|
154
128
|
data = data || {};
|
|
155
|
-
for (let key in
|
|
156
|
-
data[key] =
|
|
129
|
+
for (let key in routeResult.params)
|
|
130
|
+
data[key] = routeResult.params[key];
|
|
157
131
|
|
|
158
132
|
data.route = {
|
|
159
133
|
url: url,
|
|
160
|
-
path:
|
|
161
|
-
|
|
162
|
-
params:
|
|
134
|
+
path: routeResult.path,
|
|
135
|
+
name: routeResult.name,
|
|
136
|
+
params: routeResult.params
|
|
163
137
|
};
|
|
164
138
|
data.event = event;
|
|
165
139
|
|
|
166
|
-
let interrupt = await Promise.resolve(options.pageInterrupt(
|
|
140
|
+
let interrupt = await Promise.resolve(options.pageInterrupt(routeResult));
|
|
167
141
|
if(interrupt)
|
|
168
142
|
{
|
|
169
143
|
goal = { url, data };
|
|
@@ -173,12 +147,12 @@ async function showPage(url, data, event) {
|
|
|
173
147
|
await showLoading();
|
|
174
148
|
|
|
175
149
|
var getPage = null
|
|
176
|
-
if (pageCache[
|
|
177
|
-
getPage = pageCache[
|
|
178
|
-
else if (pageCache[
|
|
179
|
-
getPage = pageCache[
|
|
150
|
+
if (pageCache[routeResult.path])
|
|
151
|
+
getPage = pageCache[routeResult.path].page;
|
|
152
|
+
else if (pageCache[routeResult.pattern])
|
|
153
|
+
getPage = pageCache[routeResult.pattern].page;
|
|
180
154
|
else
|
|
181
|
-
getPage = loadPage(
|
|
155
|
+
getPage = loadPage(routeResult, data)
|
|
182
156
|
|
|
183
157
|
// handle initial page
|
|
184
158
|
if (event.action == 'load')
|
|
@@ -203,7 +177,7 @@ async function showPage(url, data, event) {
|
|
|
203
177
|
|
|
204
178
|
let currentState = stack[stackPointer];
|
|
205
179
|
|
|
206
|
-
if (currentState.data.route.path ==
|
|
180
|
+
if (currentState.data.route.path == routeResult.path) {
|
|
207
181
|
handleHistoryAction(event, url, data, currentState.page);
|
|
208
182
|
const page = await getPage;
|
|
209
183
|
await doUpdate(page, data);
|
|
@@ -322,26 +296,6 @@ export async function init(opts) {
|
|
|
322
296
|
}
|
|
323
297
|
}
|
|
324
298
|
|
|
325
|
-
// handle pages whose markup is already loaded in the page
|
|
326
|
-
for (var key in pageHash) {
|
|
327
|
-
if (pageHash[key].pageClass.existingDomSelector !== undefined) {
|
|
328
|
-
|
|
329
|
-
if(pageHash[key].pageClass.existingDomSelector === null)
|
|
330
|
-
continue;
|
|
331
|
-
|
|
332
|
-
let $html = document.querySelector(pageHash[key].pageClass.existingDomSelector)
|
|
333
|
-
if(!$html)
|
|
334
|
-
{
|
|
335
|
-
console.error(`Unable to find DOM element '${pageHash[key].pageClass.existingDomSelector}' for page '${key}'`)
|
|
336
|
-
continue;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// TODO: this is inefficient for non parameterised routes. There will always be HTML in memory and then copied for the page once loaded
|
|
340
|
-
pageTemplateCache[router.routesByName[key]._pattern] = $html;
|
|
341
|
-
$html.parentElement.removeChild($html);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
299
|
await initLoading();
|
|
346
300
|
|
|
347
301
|
// set initial page
|
package/src/page.js
CHANGED
package/tests/lolpack.js
CHANGED
|
@@ -12,7 +12,7 @@ import pageShowFail from './page-show-fail.js'
|
|
|
12
12
|
window.addEventListener('DOMContentLoaded', function () {
|
|
13
13
|
pageManager.init({
|
|
14
14
|
pageContainer: () => document.body,
|
|
15
|
-
fetchPath: route => '/pages/' + route.
|
|
15
|
+
fetchPath: route => '/pages/' + route.name + '.htm',
|
|
16
16
|
error404PageName: 'page404',
|
|
17
17
|
beforeHide: message => new Promise(resolve => {
|
|
18
18
|
resolve(confirm(message))
|
package/tests/page.js
CHANGED