@lightningjs/blits 2.8.8 → 2.8.9

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 CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.8.9
4
+
5
+ - Fixed focus issue related to multiple named router views
6
+
3
7
  ## 2.8.8
4
8
 
5
9
  _11 aug 2026_
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightningjs/blits",
3
- "version": "2.8.8",
3
+ "version": "2.8.9",
4
4
  "description": "Blits: The Lightning 3 App Development Framework",
5
5
  "bin": "bin/index.js",
6
6
  "exports": {
@@ -20,7 +20,7 @@ import { state } from '../router/router.js'
20
20
  import Settings from '../settings.js'
21
21
  import { DEFAULT_HOLD_TIMEOUT_MS } from '../constants.js'
22
22
  import { Log } from '../lib/log.js'
23
- import { getAncestors } from './helpers.js'
23
+ import { getAncestors, isInAliveComponentTree } from './helpers.js'
24
24
  import { platform } from '../platform.js'
25
25
 
26
26
  let focusedComponent = null
@@ -41,7 +41,7 @@ export default {
41
41
  return focusedComponent
42
42
  },
43
43
  set(component, event) {
44
- if (component === undefined || component.eol === true) return
44
+ if (component === undefined || isInAliveComponentTree(component) === false) return
45
45
 
46
46
  clearTimeout(setFocusTimeout)
47
47
 
@@ -29,3 +29,22 @@ export const getAncestors = (components) => {
29
29
  }
30
30
  return components
31
31
  }
32
+
33
+ /**
34
+ * Checks whether a component and its ancestors are alive. When root is provided,
35
+ * the component must also belong to that root's tree.
36
+ * @param {Object|undefined|null} component
37
+ * @param {Object|undefined|null} [root]
38
+ * @returns {boolean}
39
+ */
40
+ export const isInAliveComponentTree = (component, root) => {
41
+ let current = component
42
+
43
+ while (current !== undefined && current !== null) {
44
+ if (current.eol === true) return false
45
+ if (root !== undefined && current === root) return true
46
+ current = current[symbols.parent]
47
+ }
48
+
49
+ return root === undefined
50
+ }
@@ -22,6 +22,7 @@ import symbols from '../lib/symbols.js'
22
22
  import { Log } from '../lib/log.js'
23
23
  import { stage } from '../launch.js'
24
24
  import Focus from '../focus/focus.js'
25
+ import { isInAliveComponentTree } from '../focus/helpers.js'
25
26
  import Announcer from '../announcer/announcer.js'
26
27
  import Settings from '../settings.js'
27
28
  import { platform } from '../platform.js'
@@ -80,6 +81,20 @@ export const state = reactive(
80
81
  const history = []
81
82
  const routerViews = new Set()
82
83
  let singleRouterView = null
84
+ let activeNavigations = 0
85
+
86
+ const startNavigation = () => {
87
+ if (activeNavigations++ === 0) {
88
+ state.navigating = true
89
+ }
90
+ }
91
+
92
+ const finishNavigation = () => {
93
+ activeNavigations--
94
+ if (activeNavigations === 0) {
95
+ state.navigating = false
96
+ }
97
+ }
83
98
 
84
99
  // Per-RouterView navigation state keyed by router view name.
85
100
  // When multiple router views exist, each view must have its own navigation
@@ -124,9 +139,17 @@ export const navigate = async function () {
124
139
  if (!this[symbols.parent][symbols.routes] || this[symbols.parent][symbols.routes].length === 0)
125
140
  return
126
141
 
127
- if (this.history === undefined) this.history = []
142
+ startNavigation()
128
143
 
129
- state.navigating = true
144
+ try {
145
+ await performNavigation.call(this, viewState)
146
+ } finally {
147
+ finishNavigation()
148
+ }
149
+ }
150
+
151
+ const performNavigation = async function (viewState) {
152
+ if (this.history === undefined) this.history = []
130
153
 
131
154
  Announcer.stop()
132
155
  Announcer.clear()
@@ -142,8 +165,6 @@ export const navigate = async function () {
142
165
 
143
166
  // early return when route not found
144
167
  if (route === false) {
145
- state.navigating = false
146
-
147
168
  Log.error(`Route ${hash.hash} not found`)
148
169
  const routerHooks = this[symbols.parent][symbols.routerHooks]
149
170
  if (routerHooks && typeof routerHooks.error === 'function') {
@@ -154,7 +175,6 @@ export const navigate = async function () {
154
175
 
155
176
  // sameRouteObject is too simple check for now!
156
177
  if (this.currentRoute !== undefined && sameRouteObject(route, this.currentRoute)) {
157
- state.navigating = false
158
178
  return
159
179
  }
160
180
 
@@ -176,7 +196,6 @@ export const navigate = async function () {
176
196
  )
177
197
  if (beforeEachResult === false) {
178
198
  viewState.preventHashChangeNavigation = false
179
- state.navigating = false
180
199
  return
181
200
  }
182
201
 
@@ -193,7 +212,6 @@ export const navigate = async function () {
193
212
  )
194
213
  if (beforeResult === false) {
195
214
  viewState.preventHashChangeNavigation = false
196
- state.navigating = false
197
215
  return
198
216
  }
199
217
 
@@ -313,7 +331,9 @@ export const navigate = async function () {
313
331
 
314
332
  // set focus to the view that we're routing to (unless explicitly disabling passing focus)
315
333
  if (route.options.passFocus !== false) {
316
- focus ? focus.$focus() : /** @type {BlitsComponent} */ (view).$focus()
334
+ isInAliveComponentTree(focus, view)
335
+ ? /** @type {BlitsComponent} */ (focus).$focus()
336
+ : /** @type {BlitsComponent} */ (view).$focus()
317
337
  }
318
338
 
319
339
  // apply starting state of transition
@@ -400,7 +420,6 @@ export const navigate = async function () {
400
420
 
401
421
  // reset navigating indicators
402
422
  viewState.navigatingBack = false
403
- state.navigating = false
404
423
  viewState.preventHashChangeNavigation = false
405
424
  }
406
425
 
@@ -451,7 +470,6 @@ const executeBeforeHook = async function (
451
470
  platform.historyBack()
452
471
 
453
472
  viewState.navigatingBack = false
454
- state.navigating = false
455
473
  }
456
474
  return false
457
475
  }
@@ -468,7 +486,6 @@ const executeBeforeHook = async function (
468
486
  viewState.preventHashChangeNavigation = true
469
487
  platform.historyBack()
470
488
  viewState.navigatingBack = false
471
- state.navigating = false
472
489
  }
473
490
  return false
474
491
  }