@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trullock/page-manager",
3
- "version": "0.14.4",
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.2.5"
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('view-thing', '/things/{thingId}', class extends Page {
12
-
11
+ registerPage({
12
+ name: 'view-thing',
13
+ route: '/things/{thingId}',
14
+ pageClass: class extends Page { }
13
15
  });
14
16
  ```
15
17
 
16
- The first argument `'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
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
- The second argument `'/things/{thingId}'` is the url (route) of the page
22
+ `pageClass` is a class definition for the page.
19
23
 
20
- The third arguent is a class definition for the page.
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('view-thing', '/things/{thingId}', class extends Page { } );
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
- // The path to make an HTTP request to to fetch the markup, given a route
302
- fetchPath: route => '/pages/' + route.routeName + '.html',
303
-
304
- // The fetch method, you probably dont want to mess with this implementation
305
- fetchPageTemplate: route => {
306
- return fetch(options.fetchPath(route))
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[route.pattern] = $template;
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 pageHash = {},
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
- fetchPath: route => '/pages/' + route.routeName + '.html',
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 const pages = pageHash;
40
-
41
- export function registerPage(argA, argB, argC) {
24
+ export function registerPage(opts) {
42
25
 
43
- let namedRoutes = null,
44
- pageClass = null;
45
-
46
- if(argC == undefined)
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, pageClass);
60
-
61
- pageHash[name] = {
62
- route: route,
63
- pageClass: pageClass
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 entry = pageHash[options.loadingPageName];
96
- var route = router.parse(entry.route);
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 pageLookup = pageHash[options.loadingPageName];
102
- var route = router.parse(pageLookup.route);
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 pageLookup = pageHash[options.loadingPageName];
117
- var route = router.parse(pageLookup.route);
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(route, data) {
123
-
124
- var fetchPage = pageTemplateCache[route.pattern] ? Promise.resolve(pageTemplateCache[route.pattern]) : options.fetchPageTemplate(route);
125
-
126
- const $template = await fetchPage;
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
- let page = new (route.pageClass)($html);
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 = route.pageClass.cacheMarkupBy == 'path' ? route.path : route.pattern;
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
- var route = router.parse(url);
147
- if (route == null) {
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 page404 = pageHash[options.error404PageName];
151
- route = router.parse(page404.route)
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 route.params)
156
- data[key] = route.params[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: route.path,
161
- routeName: route.routeName,
162
- params: route.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(route));
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[route.path])
177
- getPage = pageCache[route.path].page;
178
- else if (pageCache[route.pattern])
179
- getPage = pageCache[route.pattern].page;
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(route, data)
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 == 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
@@ -5,8 +5,6 @@ export default class Page {
5
5
  this.visible = false;
6
6
  }
7
7
 
8
- static cacheMarkupBy = 'route'
9
-
10
8
  _title = null;
11
9
  get title() {
12
10
  return this._title;
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.routeName + '.htm',
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
@@ -1,6 +1,6 @@
1
- export default class Page {
1
+ export const cacheMarkupBy = 'route'
2
2
 
3
- static cacheMarkupBy = 'route'
3
+ export default class Page {
4
4
 
5
5
  constructor($page){
6
6
  this.$page = $page;