@trullock/page-manager 0.14.3 → 1.0.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.3",
3
+ "version": "1.0.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.pageClassLoader;
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,13 +64,13 @@ function emitUrlChanged(url)
92
64
  // TODO: 404 and error too?
93
65
  function initLoading()
94
66
  {
95
- var entry = pageHash[options.loadingPageName];
67
+ var entry = router.routesByName(options.loadingPageName);
96
68
  var route = router.parse(entry.route);
97
69
  return loadPage(route, {});
98
70
  }
99
71
 
100
72
  function showLoading() {
101
- var pageLookup = pageHash[options.loadingPageName];
73
+ var pageLookup = router.routesByName(options.loadingPageName);
102
74
  var route = router.parse(pageLookup.route);
103
75
  var data = {
104
76
  route: route,
@@ -113,27 +85,28 @@ function showLoading() {
113
85
  }
114
86
 
115
87
  function hideLoading() {
116
- var pageLookup = pageHash[options.loadingPageName];
88
+ var pageLookup = router.routesByName(options.loadingPageName);
117
89
  var route = router.parse(pageLookup.route);
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,49 +115,52 @@ 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 page404 = router.routesByName(options.error404PageName);
125
+ routeResult = router.parse(page404.route)
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 };
170
144
  return showPage(interrupt.url, null, event);
171
145
  }
172
146
 
173
- var getPage = showLoading().then(() => {
174
- if (pageCache[route.path])
175
- return pageCache[route.path].page;
176
-
177
- if (pageCache[route.pattern])
178
- return pageCache[route.pattern].page;
147
+ await showLoading();
179
148
 
180
- return loadPage(route, data)
181
- });
149
+ var getPage = null
150
+ if (pageCache[routeResult.path])
151
+ getPage = pageCache[routeResult.path].page;
152
+ else if (pageCache[routeResult.pattern])
153
+ getPage = pageCache[routeResult.pattern].page;
154
+ else
155
+ getPage = loadPage(routeResult, data)
182
156
 
183
157
  // handle initial page
184
158
  if (event.action == 'load')
185
159
  {
186
160
  const page = await getPage;
187
161
  await doShow(page, data);
162
+ await hideLoading();
163
+
188
164
  // clean initial load
189
165
  if (stackPointer == -1) {
190
166
  stack.push({ uid: 0, data, page });
@@ -201,10 +177,11 @@ async function showPage(url, data, event) {
201
177
 
202
178
  let currentState = stack[stackPointer];
203
179
 
204
- if (currentState.data.route.path == route.path) {
180
+ if (currentState.data.route.path == routeResult.path) {
205
181
  handleHistoryAction(event, url, data, currentState.page);
206
182
  const page = await getPage;
207
183
  await doUpdate(page, data);
184
+ await hideLoading();
208
185
  return page;
209
186
  }
210
187
 
@@ -225,6 +202,7 @@ async function showPage(url, data, event) {
225
202
  // else if (event.action == 'fwd')
226
203
  // history.go(-1);
227
204
  // });
205
+ await hideLoading();
228
206
  return page
229
207
  }
230
208
 
@@ -234,14 +212,12 @@ async function doShow(page, data) {
234
212
 
235
213
  await Promise.resolve(page.show(data));
236
214
  document.title = page.title;
237
- await hideLoading();
238
215
  }
239
216
 
240
217
  async function doUpdate(page, data) {
241
218
 
242
219
  await Promise.resolve(page.update(data));
243
220
  document.title = page.title
244
- await hideLoading();
245
221
  }
246
222
 
247
223
  function handleHistoryAction(event, url, data, page) {
@@ -320,26 +296,6 @@ export async function init(opts) {
320
296
  }
321
297
  }
322
298
 
323
- // handle pages whose markup is already loaded in the page
324
- for (var key in pageHash) {
325
- if (pageHash[key].pageClass.existingDomSelector !== undefined) {
326
-
327
- if(pageHash[key].pageClass.existingDomSelector === null)
328
- continue;
329
-
330
- let $html = document.querySelector(pageHash[key].pageClass.existingDomSelector)
331
- if(!$html)
332
- {
333
- console.error(`Unable to find DOM element '${pageHash[key].pageClass.existingDomSelector}' for page '${key}'`)
334
- continue;
335
- }
336
-
337
- // TODO: this is inefficient for non parameterised routes. There will always be HTML in memory and then copied for the page once loaded
338
- pageTemplateCache[router.routesByName[key]._pattern] = $html;
339
- $html.parentElement.removeChild($html);
340
- }
341
- }
342
-
343
299
  await initLoading();
344
300
 
345
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;