|
1 | | -{ /* global rilti */ |
2 | | - const {directive, each, runAsync, $, isRenderable, isProxyNode, isFunc, isStr, on, render} = rilti |
| 1 | +{ /* global rilti location */ |
| 2 | + const { directive, emitter, run, isRenderable, isFunc } = rilti |
3 | 3 |
|
4 | | - const routes = new Map() |
5 | | - routes.viewBinds = new Map() |
6 | | - routes.activeBinds = new Map() |
| 4 | + const route = emitter((route, view) => { |
| 5 | + if (view == null) return |
| 6 | + if (route[0] !== '#') route = '#' + route |
7 | 7 |
|
8 | | - const route = (name, consumer) => { |
9 | | - if (name[0] !== '#') name = '#' + name |
10 | | - |
11 | | - if (isRenderable(consumer)) { |
12 | | - if (consumer.tagName === 'TEMPLATE') { |
13 | | - const template = consumer |
14 | | - consumer = Array.from(consumer.content.childNodes) |
15 | | - template.remove() |
16 | | - } |
17 | | - if (routes.has(name)) { |
18 | | - routes.get(name).view = consumer |
19 | | - } else { |
20 | | - routes.set(name, {name, view: consumer}) |
21 | | - } |
22 | | - } else if (isFunc(consumer)) { |
23 | | - if (!routes.has(name)) routes.set(name, {name, consumers: new Set()}) |
24 | | - routes.get(name).consumers.add(consumer) |
25 | | - } |
26 | | - runAsync(() => route.activate()) |
27 | | - } |
28 | | - route.viewbind = (name, host) => { |
29 | | - if (!isStr(name) && !host) [host, name] = [name, false] |
30 | | - if (host.tagName === 'TEMPLATE') return |
31 | | - if (!isProxyNode(host)) host = $(host) |
32 | | - const viewbind = (route, active) => { |
33 | | - host.textContent = '' |
34 | | - if ('view' in route && active) render(route.view, host) |
35 | | - } |
36 | | - viewbind.revoke = () => { |
37 | | - if (name) { |
38 | | - routes.get(name).consumers.delete(viewbind) |
39 | | - routes.viewBinds.delete(host) |
40 | | - } else if (routes.activeBinds.has(host)) { |
41 | | - routes.activeBinds.delete(host) |
42 | | - } |
43 | | - } |
44 | | - if (name) { |
45 | | - route(name, viewbind) |
46 | | - routes.viewBinds.set(host, viewbind) |
47 | | - } else { |
48 | | - routes.activeBinds.set(host, viewbind) |
| 8 | + if (view.tagName === 'TEMPLATE') { |
| 9 | + view.remove() |
| 10 | + view = [...view.content.childNodes] |
49 | 11 | } |
50 | | - route.activate() |
51 | | - return viewbind |
52 | | - } |
53 | | - route.revoke = route => { |
54 | | - if ((route = routes.get(route))) { |
55 | | - if (route.consumers && route.consumers.size) { |
56 | | - each(route.consumers, consumer => { |
57 | | - if (consumer.revoke) consumer.revoke() |
58 | | - }) |
59 | | - route.consumers.clear() |
60 | | - } |
61 | | - routes.delete(route.name) |
62 | | - } |
63 | | - } |
64 | 12 |
|
65 | | - route.activate = (name = window.location.hash || '#') => { |
66 | | - if (name[0] !== '#') name = '#' + name |
67 | | - if (!routes.has(name) || name === routes.active) return |
68 | | - if (name !== window.location.hash || '#') window.location.hash = name |
69 | | - const route = routes.get(name) |
70 | | - if (route && route.consumers && route.consumers.size) { |
71 | | - each(route.consumers, consume => consume(route, true, name)) |
72 | | - } |
73 | | - if (routes.activeBinds.size) { |
74 | | - each(routes.activeBinds, bind => bind(route, true, name)) |
75 | | - } |
76 | | - if (routes.active != null) { |
77 | | - const oldroute = routes.get(routes.active) |
78 | | - if (oldroute.consumers && oldroute.consumers.size) { |
79 | | - each(oldroute.consumers, c => c(oldroute, false, routes.active)) |
80 | | - } |
| 13 | + if (isRenderable(view)) { |
| 14 | + views[route] = isFunc(view) ? view() : view |
81 | 15 | } |
82 | | - routes.active = name |
83 | | - } |
| 16 | + }) |
| 17 | + const views = route.views = Object.create(null) |
| 18 | + route.hash = () => location.hash.substr(1) |
84 | 19 |
|
85 | | - const removeVbindRoute = el => { |
86 | | - const vbind = routes.viewBinds.get(el) |
87 | | - if (vbind) vbind.revoke() |
88 | | - } |
| 20 | + directive('route-link', { |
| 21 | + init (el, val) { |
| 22 | + el.routeLink = route.on.change(() => { |
| 23 | + el.class['active-route'] = location.hash === (el.href = '#' + el.attr['route-link']) |
| 24 | + }) |
| 25 | + el.class['active-route'] = location.hash === (el.href = '#' + val) |
| 26 | + }, |
| 27 | + remove: (el, val) => el.routeLink.off() |
| 28 | + }) |
89 | 29 |
|
90 | 30 | directive('route', { |
91 | 31 | init (el, val) { |
92 | | - el.tagName === 'TEMPLATE' ? route(val, el) : route.viewbind(val, el) |
93 | | - }, |
94 | | - update (el, val) { |
95 | | - removeVbindRoute(el) |
96 | | - route.viewbind(val, el) |
| 32 | + if (el.tagName === 'TEMPLATE') { |
| 33 | + route(val, el) |
| 34 | + } else { |
| 35 | + el.routeHandler = route.on.change((view, hash) => { |
| 36 | + if (hash === el.attr.route) el.html = view |
| 37 | + else el.textContent = '' |
| 38 | + }) |
| 39 | + } |
97 | 40 | }, |
98 | | - remove: removeVbindRoute |
| 41 | + remove (el, val) { |
| 42 | + if (el.routeHandler) { |
| 43 | + el.routeHandler.off() |
| 44 | + el.textContent = '' |
| 45 | + } |
| 46 | + } |
99 | 47 | }) |
100 | 48 |
|
101 | 49 | directive('route-active', { |
102 | | - init: el => route.viewbind(el), |
103 | | - remove: removeVbindRoute |
| 50 | + init (el, val) { |
| 51 | + el.routeHandler = route.on.change((view, hash) => { |
| 52 | + el.attr['route-active'] = hash |
| 53 | + el.html = view |
| 54 | + }) |
| 55 | + }, |
| 56 | + remove (el, val) { |
| 57 | + el.routeHandler.off() |
| 58 | + el.textContent = '' |
| 59 | + } |
104 | 60 | }) |
105 | 61 |
|
106 | | - on.hashchange(window, e => route.activate()) |
| 62 | + rilti.route = route |
| 63 | + route.handle = () => { |
| 64 | + const view = route.views[location.hash] |
| 65 | + if (view == null) return |
| 66 | + route.active = view |
| 67 | + const hash = route.hash() |
| 68 | + route.emit.change(view, hash, route) |
| 69 | + route.emit[hash](view, route) |
| 70 | + } |
| 71 | + |
| 72 | + window.onhashchange = route.handle |
| 73 | + run(() => route.handle()) |
107 | 74 | } |
0 commit comments