@lightningjs/blits 2.8.2 → 2.8.4
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 +15 -0
- package/package.json +1 -1
- package/src/component/base/router.js +27 -1
- package/src/components/RouterView.js +1 -1
- package/src/engines/L3/element.js +6 -1
- package/src/router/router.js +84 -47
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
## 2.8.3
|
|
6
|
+
|
|
7
|
+
_23 jul 2026_
|
|
8
|
+
|
|
9
|
+
- Fixed issues with navigating when using multiple RouterViews
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## 2.8.3
|
|
13
|
+
|
|
14
|
+
_23 jul 2026_
|
|
15
|
+
|
|
16
|
+
- Added end of life guard to layout retrigger on node tick event
|
|
17
|
+
|
|
3
18
|
## 2.8.2
|
|
4
19
|
|
|
5
20
|
_22 jul 2026_
|
package/package.json
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import symbols from '../../lib/symbols.js'
|
|
19
|
+
import { Log } from '../../lib/log.js'
|
|
19
20
|
import {
|
|
20
21
|
back,
|
|
21
22
|
currentRoute,
|
|
@@ -67,12 +68,37 @@ const resolveRouterView = (component, routerViewName) => {
|
|
|
67
68
|
const registeredRouterView = getRegisteredRouterView(routerViewName)
|
|
68
69
|
if (registeredRouterView !== null) return registeredRouterView
|
|
69
70
|
|
|
70
|
-
|
|
71
|
+
const treeRouterView = getRouterView(component, routerViewName)
|
|
72
|
+
if (treeRouterView !== null) return treeRouterView
|
|
73
|
+
|
|
74
|
+
// Named view not found — fall back to default (unnamed) view
|
|
75
|
+
const defaultView = getRegisteredRouterView('')
|
|
76
|
+
if (defaultView !== null) {
|
|
77
|
+
Log.warn(
|
|
78
|
+
`RouterView "${routerViewName}" was not found. Navigation is falling back to the default RouterView.`
|
|
79
|
+
)
|
|
80
|
+
return defaultView
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const treeDefaultView = getRouterView(component)
|
|
84
|
+
if (treeDefaultView !== null) {
|
|
85
|
+
Log.warn(
|
|
86
|
+
`RouterView "${routerViewName}" was not found. Navigation is falling back to the default RouterView.`
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
return treeDefaultView
|
|
71
90
|
}
|
|
72
91
|
|
|
73
92
|
const singleRouterView = getSingleRegisteredRouterView()
|
|
74
93
|
if (singleRouterView !== null) return singleRouterView
|
|
75
94
|
|
|
95
|
+
// When multiple router views exist and no name was specified,
|
|
96
|
+
// prefer the default (unnamed) router view before falling back
|
|
97
|
+
// to tree-walking. This ensures hooks on the App's main router
|
|
98
|
+
// fire correctly when calling router.to() from a child component.
|
|
99
|
+
const defaultView = getRegisteredRouterView('')
|
|
100
|
+
if (defaultView !== null) return defaultView
|
|
101
|
+
|
|
76
102
|
return getRouterView(component)
|
|
77
103
|
}
|
|
78
104
|
|
|
@@ -152,7 +152,11 @@ const layoutFn = function (config) {
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
// trigger layout on parent if parent is a layout
|
|
155
|
-
if (
|
|
155
|
+
if (
|
|
156
|
+
this.config.parent &&
|
|
157
|
+
this.config.parent.eol !== true &&
|
|
158
|
+
this.config.parent.props.__layout === true
|
|
159
|
+
) {
|
|
156
160
|
this.config.parent.triggerLayout(this.config.parent.props)
|
|
157
161
|
}
|
|
158
162
|
}
|
|
@@ -907,6 +911,7 @@ const Element = {
|
|
|
907
911
|
|
|
908
912
|
if (this.config.parent.props && this.config.parent.props.__layout === true) {
|
|
909
913
|
f.on('tick', () => {
|
|
914
|
+
if (this.eol === true || !this.config) return
|
|
910
915
|
this.config.parent.triggerLayout(this.config.parent.props)
|
|
911
916
|
})
|
|
912
917
|
}
|
package/src/router/router.js
CHANGED
|
@@ -81,14 +81,28 @@ const history = []
|
|
|
81
81
|
const routerViews = new Set()
|
|
82
82
|
let singleRouterView = null
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
84
|
+
// Per-RouterView navigation state keyed by router view name.
|
|
85
|
+
// When multiple router views exist, each view must have its own navigation
|
|
86
|
+
// state so that one view's navigation doesn't interfere with another's hooks.
|
|
87
|
+
const perViewState = new Map()
|
|
88
|
+
|
|
89
|
+
const getViewState = (name = '') => {
|
|
90
|
+
if (!perViewState.has(name)) {
|
|
91
|
+
perViewState.set(name, {
|
|
92
|
+
overrideOptions: {},
|
|
93
|
+
navigationData: {},
|
|
94
|
+
navigatingBack: false,
|
|
95
|
+
navigatingBackTo: undefined,
|
|
96
|
+
previousFocus: undefined,
|
|
97
|
+
preventHashChangeNavigation: false,
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
return perViewState.get(name)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const clearViewState = (name = '') => {
|
|
104
|
+
perViewState.delete(name)
|
|
105
|
+
}
|
|
92
106
|
|
|
93
107
|
/**
|
|
94
108
|
* Navigate to a route
|
|
@@ -103,8 +117,9 @@ let preventHashChangeNavigation = false
|
|
|
103
117
|
* @returns {Promise<void>}
|
|
104
118
|
*/
|
|
105
119
|
export const navigate = async function () {
|
|
120
|
+
const viewState = getViewState(this.name)
|
|
106
121
|
// early return when in preventHashChange mode
|
|
107
|
-
if (preventHashChangeNavigation !== false) return
|
|
122
|
+
if (viewState.preventHashChangeNavigation !== false) return
|
|
108
123
|
// early return when no routes
|
|
109
124
|
if (!this[symbols.parent][symbols.routes] || this[symbols.parent][symbols.routes].length === 0)
|
|
110
125
|
return
|
|
@@ -118,7 +133,12 @@ export const navigate = async function () {
|
|
|
118
133
|
|
|
119
134
|
const hash = getHash(location.hash, this.name)
|
|
120
135
|
// try to find the route
|
|
121
|
-
let route = matchHash(
|
|
136
|
+
let route = matchHash(
|
|
137
|
+
hash,
|
|
138
|
+
this[symbols.parent][symbols.routes],
|
|
139
|
+
viewState.overrideOptions,
|
|
140
|
+
viewState.navigationData
|
|
141
|
+
)
|
|
122
142
|
|
|
123
143
|
// early return when route not found
|
|
124
144
|
if (route === false) {
|
|
@@ -151,10 +171,11 @@ export const navigate = async function () {
|
|
|
151
171
|
this[symbols.parent],
|
|
152
172
|
route,
|
|
153
173
|
this.previousRoute,
|
|
154
|
-
currentPath
|
|
174
|
+
currentPath,
|
|
175
|
+
viewState
|
|
155
176
|
)
|
|
156
177
|
if (beforeEachResult === false) {
|
|
157
|
-
preventHashChangeNavigation = false
|
|
178
|
+
viewState.preventHashChangeNavigation = false
|
|
158
179
|
state.navigating = false
|
|
159
180
|
return
|
|
160
181
|
}
|
|
@@ -167,10 +188,11 @@ export const navigate = async function () {
|
|
|
167
188
|
this[symbols.parent],
|
|
168
189
|
route,
|
|
169
190
|
this.previousRoute,
|
|
170
|
-
currentPath
|
|
191
|
+
currentPath,
|
|
192
|
+
viewState
|
|
171
193
|
)
|
|
172
194
|
if (beforeResult === false) {
|
|
173
|
-
preventHashChangeNavigation = false
|
|
195
|
+
viewState.preventHashChangeNavigation = false
|
|
174
196
|
state.navigating = false
|
|
175
197
|
return
|
|
176
198
|
}
|
|
@@ -184,7 +206,7 @@ export const navigate = async function () {
|
|
|
184
206
|
this.previousRoute &&
|
|
185
207
|
this.previousRoute.options &&
|
|
186
208
|
this.previousRoute.options.inHistory === true &&
|
|
187
|
-
navigatingBack === false
|
|
209
|
+
viewState.navigatingBack === false
|
|
188
210
|
) {
|
|
189
211
|
this.history.push(this.previousRoute)
|
|
190
212
|
}
|
|
@@ -202,10 +224,10 @@ export const navigate = async function () {
|
|
|
202
224
|
let view
|
|
203
225
|
let focus
|
|
204
226
|
// when navigating back let's see if we're navigating back to a route that was kept alive
|
|
205
|
-
if (navigatingBack === true && navigatingBackTo !== undefined) {
|
|
206
|
-
view = navigatingBackTo.view
|
|
207
|
-
focus = navigatingBackTo.focus
|
|
208
|
-
navigatingBackTo = null
|
|
227
|
+
if (viewState.navigatingBack === true && viewState.navigatingBackTo !== undefined) {
|
|
228
|
+
view = viewState.navigatingBackTo.view
|
|
229
|
+
focus = viewState.navigatingBackTo.focus
|
|
230
|
+
viewState.navigatingBackTo = null
|
|
209
231
|
}
|
|
210
232
|
// merge props with potential route params, navigation data and route data to be injected into the component instance
|
|
211
233
|
const props = {
|
|
@@ -284,7 +306,7 @@ export const navigate = async function () {
|
|
|
284
306
|
}
|
|
285
307
|
|
|
286
308
|
// keep reference to the previous focus for storing in cache
|
|
287
|
-
previousFocus = Focus.get()
|
|
309
|
+
viewState.previousFocus = Focus.get()
|
|
288
310
|
|
|
289
311
|
const children = this[symbols.children]
|
|
290
312
|
this.activeView = children[children.length - 1]
|
|
@@ -313,15 +335,16 @@ export const navigate = async function () {
|
|
|
313
335
|
|
|
314
336
|
// Resolve effective keepAlive: runtime override from $router.to() takes precedence
|
|
315
337
|
// over the static route config option
|
|
316
|
-
const hasOverrideOptions =
|
|
338
|
+
const hasOverrideOptions =
|
|
339
|
+
viewState.overrideOptions && typeof viewState.overrideOptions === 'object'
|
|
317
340
|
const keepAlive =
|
|
318
|
-
hasOverrideOptions && overrideOptions.keepAlive !== undefined
|
|
319
|
-
? overrideOptions.keepAlive
|
|
341
|
+
hasOverrideOptions && viewState.overrideOptions.keepAlive !== undefined
|
|
342
|
+
? viewState.overrideOptions.keepAlive
|
|
320
343
|
: this.previousRoute.options && this.previousRoute.options.keepAlive
|
|
321
344
|
|
|
322
345
|
// cache the page when it's as 'keepAlive' instead of destroying
|
|
323
346
|
if (
|
|
324
|
-
navigatingBack === false &&
|
|
347
|
+
viewState.navigatingBack === false &&
|
|
325
348
|
keepAlive === true &&
|
|
326
349
|
this.previousRoute &&
|
|
327
350
|
this.previousRoute.options &&
|
|
@@ -330,7 +353,7 @@ export const navigate = async function () {
|
|
|
330
353
|
const historyItem = this.history[this.history.length - 1]
|
|
331
354
|
if (historyItem !== undefined) {
|
|
332
355
|
historyItem.view = oldView
|
|
333
|
-
historyItem.focus = previousFocus
|
|
356
|
+
historyItem.focus = viewState.previousFocus
|
|
334
357
|
}
|
|
335
358
|
}
|
|
336
359
|
|
|
@@ -339,12 +362,12 @@ export const navigate = async function () {
|
|
|
339
362
|
* 2. Navigating back, and the previous route is configured with "keep alive" set to true.
|
|
340
363
|
* 3. Navigating back, and the previous route is not configured with "keep alive" set to true.
|
|
341
364
|
*/
|
|
342
|
-
if (this.previousRoute.options && (keepAlive !== true || navigatingBack === true)) {
|
|
365
|
+
if (this.previousRoute.options && (keepAlive !== true || viewState.navigatingBack === true)) {
|
|
343
366
|
oldView.destroy()
|
|
344
367
|
oldView = null
|
|
345
368
|
}
|
|
346
369
|
|
|
347
|
-
previousFocus = null
|
|
370
|
+
viewState.previousFocus = null
|
|
348
371
|
}
|
|
349
372
|
}
|
|
350
373
|
|
|
@@ -363,16 +386,14 @@ export const navigate = async function () {
|
|
|
363
386
|
// execute after route Hook
|
|
364
387
|
await executeAfterHook(route.hooks, 'after', this[symbols.parent], route, this.previousRoute)
|
|
365
388
|
|
|
366
|
-
// Clear
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
overrideOptions = {}
|
|
370
|
-
navigationData = {}
|
|
389
|
+
// Clear per-view navigation state after navigation is complete.
|
|
390
|
+
viewState.overrideOptions = {}
|
|
391
|
+
viewState.navigationData = {}
|
|
371
392
|
|
|
372
393
|
// reset navigating indicators
|
|
373
|
-
navigatingBack = false
|
|
394
|
+
viewState.navigatingBack = false
|
|
374
395
|
state.navigating = false
|
|
375
|
-
preventHashChangeNavigation = false
|
|
396
|
+
viewState.preventHashChangeNavigation = false
|
|
376
397
|
}
|
|
377
398
|
|
|
378
399
|
const setOrAnimate = (element, transition, shouldAnimate = true) => {
|
|
@@ -402,7 +423,8 @@ const executeBeforeHook = async function (
|
|
|
402
423
|
parent,
|
|
403
424
|
route,
|
|
404
425
|
previousRoute,
|
|
405
|
-
currentPath
|
|
426
|
+
currentPath,
|
|
427
|
+
viewState
|
|
406
428
|
) {
|
|
407
429
|
let result
|
|
408
430
|
if (hooks && hooks[hookName]) {
|
|
@@ -417,10 +439,10 @@ const executeBeforeHook = async function (
|
|
|
417
439
|
Log.error(`Error or Rejected Promise in "${hookName}" Hook`, error)
|
|
418
440
|
this.currentRoute = previousRoute
|
|
419
441
|
if (this.history.length > 0) {
|
|
420
|
-
preventHashChangeNavigation = true
|
|
442
|
+
viewState.preventHashChangeNavigation = true
|
|
421
443
|
platform.historyBack()
|
|
422
444
|
|
|
423
|
-
navigatingBack = false
|
|
445
|
+
viewState.navigatingBack = false
|
|
424
446
|
state.navigating = false
|
|
425
447
|
}
|
|
426
448
|
return false
|
|
@@ -435,9 +457,9 @@ const executeBeforeHook = async function (
|
|
|
435
457
|
if (result === false) {
|
|
436
458
|
this.currentRoute = previousRoute
|
|
437
459
|
if (this.history.length > 0) {
|
|
438
|
-
preventHashChangeNavigation = true
|
|
460
|
+
viewState.preventHashChangeNavigation = true
|
|
439
461
|
platform.historyBack()
|
|
440
|
-
navigatingBack = false
|
|
462
|
+
viewState.navigatingBack = false
|
|
441
463
|
state.navigating = false
|
|
442
464
|
}
|
|
443
465
|
return false
|
|
@@ -493,20 +515,33 @@ const executeTransition = async (transition, element, animate) => {
|
|
|
493
515
|
}
|
|
494
516
|
|
|
495
517
|
export const to = (path, data = {}, options = {}, routerViewName = '') => {
|
|
496
|
-
|
|
497
|
-
|
|
518
|
+
const viewState = getViewState(routerViewName)
|
|
519
|
+
viewState.navigationData = data
|
|
520
|
+
viewState.overrideOptions = options
|
|
498
521
|
|
|
499
522
|
setHash(path, routerViewName)
|
|
500
523
|
}
|
|
501
524
|
|
|
502
525
|
export const toRouterView = (routerView, path, data = {}, options = {}) => {
|
|
503
|
-
|
|
504
|
-
|
|
526
|
+
const viewState = getViewState(routerView.name)
|
|
527
|
+
viewState.navigationData = data
|
|
528
|
+
viewState.overrideOptions = options
|
|
505
529
|
|
|
506
530
|
setHash(path, routerView.name)
|
|
507
531
|
}
|
|
508
532
|
|
|
509
533
|
export const registerRouterView = (routerView) => {
|
|
534
|
+
// RouterView names identify both the view and its navigation state, so duplicate
|
|
535
|
+
// names cause state collisions and ambiguous route resolution.
|
|
536
|
+
for (const existing of routerViews) {
|
|
537
|
+
if (existing !== routerView && existing.name === routerView.name) {
|
|
538
|
+
const name = routerView.name === '' ? 'the default name' : `"${routerView.name}"`
|
|
539
|
+
Log.warn(
|
|
540
|
+
`Multiple RouterViews use ${name}. Each RouterView must have a unique and stable "name" prop to ensure correct routing behavior.`
|
|
541
|
+
)
|
|
542
|
+
break
|
|
543
|
+
}
|
|
544
|
+
}
|
|
510
545
|
routerViews.add(routerView)
|
|
511
546
|
singleRouterView = routerViews.size === 1 ? routerView : null
|
|
512
547
|
}
|
|
@@ -514,6 +549,7 @@ export const registerRouterView = (routerView) => {
|
|
|
514
549
|
export const unregisterRouterView = (routerView) => {
|
|
515
550
|
routerViews.delete(routerView)
|
|
516
551
|
singleRouterView = routerViews.size === 1 ? routerViews.values().next().value : null
|
|
552
|
+
clearViewState(routerView.name)
|
|
517
553
|
}
|
|
518
554
|
|
|
519
555
|
export const getRegisteredRouterView = (name) => {
|
|
@@ -531,11 +567,12 @@ export const getSingleRegisteredRouterView = () => {
|
|
|
531
567
|
|
|
532
568
|
export const back = function () {
|
|
533
569
|
if (this.history === undefined) return
|
|
570
|
+
const viewState = getViewState(this.name)
|
|
534
571
|
const route = this.history.pop()
|
|
535
572
|
if (route && this.currentRoute !== route) {
|
|
536
573
|
// set indicator that we are navigating back (to prevent adding page to history stack)
|
|
537
|
-
navigatingBack = true
|
|
538
|
-
navigatingBackTo = route
|
|
574
|
+
viewState.navigatingBack = true
|
|
575
|
+
viewState.navigatingBackTo = route
|
|
539
576
|
toRouterView(this, route.hash, route.data, route.options)
|
|
540
577
|
return true
|
|
541
578
|
}
|
|
@@ -567,8 +604,8 @@ export const back = function () {
|
|
|
567
604
|
const route = matchHash(
|
|
568
605
|
getHash(path),
|
|
569
606
|
this[symbols.parent][symbols.routes],
|
|
570
|
-
overrideOptions,
|
|
571
|
-
navigationData
|
|
607
|
+
viewState.overrideOptions,
|
|
608
|
+
viewState.navigationData
|
|
572
609
|
)
|
|
573
610
|
|
|
574
611
|
if (route && backtrack) {
|