@lightningjs/blits 2.2.0 → 2.4.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/CHANGELOG.md +14 -0
- package/index.d.ts +13 -2
- package/package.json +1 -1
- package/src/announcer/announcer.js +1 -1
- package/src/announcer/speechSynthesis.js +1 -1
- package/src/component/base/router.js +136 -22
- package/src/components/RouterView.js +27 -7
- package/src/focus/focus.js +1 -0
- package/src/lib/reactivityguard/computedprops.js +235 -153
- package/src/router/router.js +381 -503
- package/src/router/utils.js +245 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v2.4.0
|
|
4
|
+
|
|
5
|
+
_1 june 2026_
|
|
6
|
+
|
|
7
|
+
- Improved correct registration of reactivity in computed props
|
|
8
|
+
- Added named router views to enable multiple nested router views
|
|
9
|
+
|
|
10
|
+
## v2.3.0
|
|
11
|
+
|
|
12
|
+
_20 may 2026_
|
|
13
|
+
|
|
14
|
+
- Refactored router functionality
|
|
15
|
+
- Replaced `window` reference with `self` reference in announcer
|
|
16
|
+
|
|
3
17
|
## v2.2.0
|
|
4
18
|
|
|
5
19
|
_11 may 2026_
|
package/index.d.ts
CHANGED
|
@@ -329,7 +329,14 @@ declare module '@lightningjs/blits' {
|
|
|
329
329
|
*
|
|
330
330
|
* @param {string}
|
|
331
331
|
*/
|
|
332
|
-
to(location: string, data?: RouteData, options?: RouteOptions):
|
|
332
|
+
to(location: string, data?: RouteData, options?: RouteOptions): boolean;
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Get a router scoped to a named RouterView
|
|
336
|
+
*
|
|
337
|
+
* @param {string}
|
|
338
|
+
*/
|
|
339
|
+
get(name: string): Router;
|
|
333
340
|
|
|
334
341
|
/**
|
|
335
342
|
* Navigate to the previous location
|
|
@@ -707,6 +714,10 @@ declare module '@lightningjs/blits' {
|
|
|
707
714
|
* Watchers for changes to state variables, props or computed properties
|
|
708
715
|
*/
|
|
709
716
|
watch?: W & ComponentContext<P, S, M, C>
|
|
717
|
+
/**
|
|
718
|
+
* Router Configuration
|
|
719
|
+
*/
|
|
720
|
+
router?: RouterConfig<P, S, M, C>
|
|
710
721
|
}
|
|
711
722
|
|
|
712
723
|
export interface RouterHooks {
|
|
@@ -759,7 +770,7 @@ declare module '@lightningjs/blits' {
|
|
|
759
770
|
|
|
760
771
|
export type ApplicationConfig<P extends Props, S, M, C, W> = Omit<
|
|
761
772
|
ComponentConfig<P, S, M, C, W>,
|
|
762
|
-
'hooks' | 'methods' | 'input' | 'computed' | 'watch' | 'state'
|
|
773
|
+
'hooks' | 'methods' | 'input' | 'computed' | 'watch' | 'state' | 'router' | 'routes'
|
|
763
774
|
> & {
|
|
764
775
|
hooks?: Hooks & ApplicationContext<P, S, M, C>
|
|
765
776
|
methods?: M & ApplicationContext<P, S, M, C>
|
package/package.json
CHANGED
|
@@ -27,7 +27,7 @@ let debounce = null
|
|
|
27
27
|
|
|
28
28
|
// Global default utterance options
|
|
29
29
|
let globalDefaultOptions = {
|
|
30
|
-
enableUtteranceKeepAlive: !/android/i.test((
|
|
30
|
+
enableUtteranceKeepAlive: !/android/i.test((self.navigator || {}).userAgent || ''),
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
const noopAnnouncement = {
|
|
@@ -16,31 +16,145 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import symbols from '../../lib/symbols.js'
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
back,
|
|
21
|
+
currentRoute,
|
|
22
|
+
getRegisteredRouterView,
|
|
23
|
+
getSingleRegisteredRouterView,
|
|
24
|
+
state,
|
|
25
|
+
toRouterView,
|
|
26
|
+
} from '../../router/router.js'
|
|
27
|
+
|
|
28
|
+
const routerCache = new WeakMap()
|
|
29
|
+
|
|
30
|
+
const isRouterView = (component, routerViewName = '') => {
|
|
31
|
+
if (component === undefined || component === null) return false
|
|
32
|
+
return (
|
|
33
|
+
Array.isArray(component.history) && (routerViewName === '' || component.name === routerViewName)
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const getRouterViewInChildren = (component, routerViewName = '') => {
|
|
38
|
+
if (component === undefined || component === null) return null
|
|
39
|
+
|
|
40
|
+
const children = component[symbols.children]
|
|
41
|
+
if (Array.isArray(children) === false) return null
|
|
42
|
+
|
|
43
|
+
for (let i = 0; i < children.length; i++) {
|
|
44
|
+
const child = children[i]
|
|
45
|
+
if (isRouterView(child, routerViewName)) return child
|
|
46
|
+
|
|
47
|
+
const nestedRouterView = getRouterViewInChildren(child, routerViewName)
|
|
48
|
+
if (nestedRouterView !== null) return nestedRouterView
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const getRouterView = (component, routerViewName = '') => {
|
|
55
|
+
let current = component
|
|
56
|
+
while (current !== undefined && current !== null) {
|
|
57
|
+
if (isRouterView(current, routerViewName)) {
|
|
58
|
+
return current
|
|
59
|
+
}
|
|
60
|
+
current = current[symbols.parent]
|
|
61
|
+
}
|
|
62
|
+
return getRouterViewInChildren(component, routerViewName)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const resolveRouterView = (component, routerViewName) => {
|
|
66
|
+
if (routerViewName !== undefined) {
|
|
67
|
+
const registeredRouterView = getRegisteredRouterView(routerViewName)
|
|
68
|
+
if (registeredRouterView !== null) return registeredRouterView
|
|
69
|
+
|
|
70
|
+
return getRouterView(component, routerViewName)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const singleRouterView = getSingleRegisteredRouterView()
|
|
74
|
+
if (singleRouterView !== null) return singleRouterView
|
|
75
|
+
|
|
76
|
+
return getRouterView(component)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const getRoutes = (component, routerViewName) => {
|
|
80
|
+
const routerView = resolveRouterView(component, routerViewName)
|
|
81
|
+
if (
|
|
82
|
+
routerView !== null &&
|
|
83
|
+
routerView[symbols.parent] !== undefined &&
|
|
84
|
+
routerView[symbols.parent][symbols.routes] !== undefined
|
|
85
|
+
) {
|
|
86
|
+
return routerView[symbols.parent][symbols.routes]
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (component[symbols.routes] !== undefined) return component[symbols.routes]
|
|
90
|
+
|
|
91
|
+
if (
|
|
92
|
+
component[symbols.parent] !== undefined &&
|
|
93
|
+
component[symbols.parent][symbols.routes] !== undefined
|
|
94
|
+
) {
|
|
95
|
+
return component[symbols.parent][symbols.routes]
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return undefined
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const getRouterFromCache = (component, routerViewName) => {
|
|
102
|
+
let routers = routerCache.get(component)
|
|
103
|
+
if (routers === undefined) {
|
|
104
|
+
routers = new Map()
|
|
105
|
+
routerCache.set(component, routers)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const cacheKey = routerViewName === undefined ? '' : routerViewName
|
|
109
|
+
const router = routers.get(cacheKey)
|
|
110
|
+
if (router !== undefined) return router
|
|
111
|
+
|
|
112
|
+
const newRouter = createRouter(component, routerViewName)
|
|
113
|
+
routers.set(cacheKey, newRouter)
|
|
114
|
+
return newRouter
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const createRouter = (component, routerViewName) => {
|
|
118
|
+
return {
|
|
119
|
+
to(path, data = {}, options = {}) {
|
|
120
|
+
const routerView = resolveRouterView(component, routerViewName)
|
|
121
|
+
if (routerView === null) return false
|
|
122
|
+
|
|
123
|
+
toRouterView(routerView, path, data, options)
|
|
124
|
+
return true
|
|
125
|
+
},
|
|
126
|
+
back() {
|
|
127
|
+
const routerView = resolveRouterView(component, routerViewName)
|
|
128
|
+
return routerView === null ? false : back.call(routerView)
|
|
129
|
+
},
|
|
130
|
+
get(name) {
|
|
131
|
+
return getRouterFromCache(component, name)
|
|
132
|
+
},
|
|
133
|
+
get backNavigation() {
|
|
134
|
+
return state.backNavigation !== false
|
|
135
|
+
},
|
|
136
|
+
set backNavigation(enabled) {
|
|
137
|
+
state.backNavigation = enabled !== false
|
|
138
|
+
},
|
|
139
|
+
get currentRoute() {
|
|
140
|
+
const routerView = resolveRouterView(component, routerViewName)
|
|
141
|
+
return routerView === null ? currentRoute : routerView.currentRoute
|
|
142
|
+
},
|
|
143
|
+
get routes() {
|
|
144
|
+
return getRoutes(component, routerViewName)
|
|
145
|
+
},
|
|
146
|
+
get navigating() {
|
|
147
|
+
return state.navigating
|
|
148
|
+
},
|
|
149
|
+
state,
|
|
150
|
+
}
|
|
151
|
+
}
|
|
20
152
|
|
|
21
153
|
export default {
|
|
22
154
|
$router: {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
get backNavigation() {
|
|
27
|
-
return state.backNavigation !== false
|
|
28
|
-
},
|
|
29
|
-
set backNavigation(enabled) {
|
|
30
|
-
state.backNavigation = enabled !== false
|
|
31
|
-
},
|
|
32
|
-
get currentRoute() {
|
|
33
|
-
return currentRoute
|
|
34
|
-
},
|
|
35
|
-
get routes() {
|
|
36
|
-
return this[symbols.routes]
|
|
37
|
-
},
|
|
38
|
-
get navigating() {
|
|
39
|
-
return state.navigating
|
|
40
|
-
},
|
|
41
|
-
state,
|
|
42
|
-
},
|
|
43
|
-
writable: false,
|
|
155
|
+
get() {
|
|
156
|
+
return getRouterFromCache(this)
|
|
157
|
+
},
|
|
44
158
|
enumerable: true,
|
|
45
159
|
configurable: false,
|
|
46
160
|
},
|
|
@@ -16,12 +16,14 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import Component from '../component.js'
|
|
19
|
-
import Router, {
|
|
19
|
+
import Router, {
|
|
20
|
+
registerRouterView,
|
|
21
|
+
state as routerState,
|
|
22
|
+
unregisterRouterView,
|
|
23
|
+
} from '../router/router.js'
|
|
20
24
|
import symbols from '../lib/symbols.js'
|
|
21
25
|
import Focus from '../focus/focus.js'
|
|
22
26
|
|
|
23
|
-
let hashchangeHandler = null
|
|
24
|
-
|
|
25
27
|
/** @typedef {{ $input?: (event: any) => boolean, $focus?: (event: any) => void }} RouterViewParent */
|
|
26
28
|
|
|
27
29
|
export default () =>
|
|
@@ -123,24 +125,42 @@ export default () =>
|
|
|
123
125
|
effects: [],
|
|
124
126
|
context: {},
|
|
125
127
|
},
|
|
128
|
+
props: {
|
|
129
|
+
name: '',
|
|
130
|
+
active: true,
|
|
131
|
+
},
|
|
126
132
|
state() {
|
|
127
133
|
return {
|
|
128
134
|
activeView: null,
|
|
135
|
+
history: [],
|
|
129
136
|
}
|
|
130
137
|
},
|
|
138
|
+
watch: {
|
|
139
|
+
active(v, old) {
|
|
140
|
+
if (v === true && old === false) {
|
|
141
|
+
Router.navigate.apply(this)
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
},
|
|
131
145
|
hooks: {
|
|
132
146
|
async ready() {
|
|
147
|
+
registerRouterView(this)
|
|
148
|
+
|
|
133
149
|
const parent = this[symbols.parent]
|
|
134
150
|
if (parent && parent[symbols.routerHooks] && parent[symbols.routerHooks].init) {
|
|
135
151
|
await parent[symbols.routerHooks].init.apply(parent)
|
|
136
152
|
}
|
|
153
|
+
this.hashchangeHandler = () => Router.navigate.apply(this)
|
|
154
|
+
|
|
155
|
+
if (this.active === true) {
|
|
156
|
+
Router.navigate.apply(this)
|
|
157
|
+
}
|
|
137
158
|
|
|
138
|
-
|
|
139
|
-
Router.navigate.apply(this)
|
|
140
|
-
window.addEventListener('hashchange', hashchangeHandler)
|
|
159
|
+
window.addEventListener('hashchange', this.hashchangeHandler)
|
|
141
160
|
},
|
|
142
161
|
destroy() {
|
|
143
|
-
|
|
162
|
+
unregisterRouterView(this)
|
|
163
|
+
window.removeEventListener('hashchange', this.hashchangeHandler, false)
|
|
144
164
|
},
|
|
145
165
|
focus() {
|
|
146
166
|
if (this.activeView && Focus.get() === this) {
|