@lightningjs/blits 1.31.1 → 1.32.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 +18 -1
- package/index.d.ts +16 -3
- package/package.json +2 -2
- package/src/application.js +20 -0
- package/src/components/RouterView.js +5 -1
- package/src/engines/L3/element.js +16 -11
- package/src/focus.js +4 -1
- package/src/lib/symbols.js +2 -0
- package/src/router/router.js +23 -21
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
_02 Jul 2025_
|
|
4
|
+
|
|
5
|
+
## v1.32.0
|
|
6
|
+
|
|
7
|
+
- Added global router hooks `init` and `error`
|
|
8
|
+
- Fixed issues with `this` scope in global router hooks
|
|
9
|
+
- Fixed issue with `data` in `beforeEach` route not being complete
|
|
10
|
+
- Added input throttling functionality
|
|
11
|
+
- Optimized performance of children retrieval in `<Layout>`
|
|
12
|
+
- Added documentation on global router hooks
|
|
13
|
+
|
|
14
|
+
_30 Jun 2025_
|
|
15
|
+
|
|
16
|
+
## v1.31.1
|
|
17
|
+
|
|
18
|
+
- Upgraded renderer to v2.15.0
|
|
19
|
+
|
|
20
|
+
_30 Jun 2025_
|
|
4
21
|
|
|
5
22
|
## v1.31.1
|
|
6
23
|
|
package/index.d.ts
CHANGED
|
@@ -481,14 +481,16 @@ declare module '@lightningjs/blits' {
|
|
|
481
481
|
}
|
|
482
482
|
|
|
483
483
|
export interface RouterHooks {
|
|
484
|
+
init?: () => Promise<> | void;
|
|
484
485
|
beforeEach?: (to: Route, from: Route) => string | Route | Promise<string | Route> | void;
|
|
486
|
+
error?: (err: string) => string | Route | Promise<string | Route> | void;
|
|
485
487
|
}
|
|
486
488
|
|
|
487
|
-
export interface RouterConfig {
|
|
489
|
+
export interface RouterConfig<P extends Props, S, M, C> {
|
|
488
490
|
/**
|
|
489
491
|
* Register hooks for the router
|
|
490
492
|
*/
|
|
491
|
-
hooks?: RouterHooks,
|
|
493
|
+
hooks?: RouterHooks & ComponentContext<P, S, M, C>,
|
|
492
494
|
|
|
493
495
|
/**
|
|
494
496
|
* Routes definition
|
|
@@ -511,7 +513,7 @@ declare module '@lightningjs/blits' {
|
|
|
511
513
|
/**
|
|
512
514
|
* Router Configuration
|
|
513
515
|
*/
|
|
514
|
-
router?: RouterConfig,
|
|
516
|
+
router?: RouterConfig<P, S, M, C>,
|
|
515
517
|
routes?: never
|
|
516
518
|
}
|
|
517
519
|
|
|
|
@@ -965,6 +967,17 @@ declare module '@lightningjs/blits' {
|
|
|
965
967
|
* Defaults to `50` (ms)
|
|
966
968
|
*/
|
|
967
969
|
holdTimeout?: number,
|
|
970
|
+
/**
|
|
971
|
+
* Input throttle time in milliseconds to prevent rapid successive inputs
|
|
972
|
+
*
|
|
973
|
+
* Within the throttle window, only one input will be processed immediately Subsequent
|
|
974
|
+
* inputs are ignored until the scheduled input is processed.
|
|
975
|
+
*
|
|
976
|
+
* Set to `0` to disable input throttling.
|
|
977
|
+
*
|
|
978
|
+
* Defaults to `0` (disabled)
|
|
979
|
+
*/
|
|
980
|
+
inputThrottle?: number,
|
|
968
981
|
/**
|
|
969
982
|
* Custom canvas object used to render the App to.
|
|
970
983
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightningjs/blits",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.32.0",
|
|
4
4
|
"description": "Blits: The Lightning 3 App Development Framework",
|
|
5
5
|
"bin": "bin/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@lightningjs/msdf-generator": "^1.1.1",
|
|
51
|
-
"@lightningjs/renderer": "^2.
|
|
51
|
+
"@lightningjs/renderer": "^2.15.0"
|
|
52
52
|
},
|
|
53
53
|
"repository": {
|
|
54
54
|
"type": "git",
|
package/src/application.js
CHANGED
|
@@ -47,6 +47,7 @@ const Application = (config) => {
|
|
|
47
47
|
let keyDownHandler
|
|
48
48
|
let keyUpHandler
|
|
49
49
|
let holdTimeout
|
|
50
|
+
let lastInputTime = 0
|
|
50
51
|
|
|
51
52
|
config.hooks[symbols.destroy] = function () {
|
|
52
53
|
document.removeEventListener('keydown', keyDownHandler)
|
|
@@ -58,8 +59,27 @@ const Application = (config) => {
|
|
|
58
59
|
this.$announcer.toggle(Settings.get('announcer', false))
|
|
59
60
|
const keyMap = { ...defaultKeyMap, ...Settings.get('keymap', {}) }
|
|
60
61
|
|
|
62
|
+
/** @type {number} Input throttle time in milliseconds (0 = disabled) */
|
|
63
|
+
const throttleMs = Settings.get('inputThrottle', 0)
|
|
64
|
+
|
|
61
65
|
keyDownHandler = async (e) => {
|
|
66
|
+
const currentTime = performance.now()
|
|
67
|
+
|
|
62
68
|
const key = keyMap[e.key] || keyMap[e.keyCode] || e.key || e.keyCode
|
|
69
|
+
// execute immediately when no throttle is specified or event is internal (bubbled up by focus manager)
|
|
70
|
+
if (throttleMs === 0 || e[symbols.internalEvent] === true) {
|
|
71
|
+
return await processInput.call(this, e, key)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (currentTime - lastInputTime < throttleMs) {
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
lastInputTime = currentTime
|
|
79
|
+
await processInput.call(this, e, key)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const processInput = async function (e, key) {
|
|
63
83
|
// intercept key press if specified in main Application component
|
|
64
84
|
if (
|
|
65
85
|
this[symbols.inputEvents] !== undefined &&
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import Component from '../component.js'
|
|
19
19
|
import Router from '../router/router.js'
|
|
20
|
+
import symbols from '../lib/symbols.js'
|
|
20
21
|
|
|
21
22
|
let hashchangeHandler = null
|
|
22
23
|
|
|
@@ -58,7 +59,10 @@ export default () =>
|
|
|
58
59
|
}
|
|
59
60
|
},
|
|
60
61
|
hooks: {
|
|
61
|
-
ready() {
|
|
62
|
+
async ready() {
|
|
63
|
+
if (this.parent[symbols.routerHooks] && this.parent[symbols.routerHooks].init) {
|
|
64
|
+
await this.parent[symbols.routerHooks].init.apply(this.parent)
|
|
65
|
+
}
|
|
62
66
|
hashchangeHandler = () => Router.navigate.apply(this)
|
|
63
67
|
Router.navigate.apply(this)
|
|
64
68
|
window.addEventListener('hashchange', hashchangeHandler)
|
|
@@ -22,9 +22,6 @@ import { Log } from '../../lib/log.js'
|
|
|
22
22
|
import symbols from '../../lib/symbols.js'
|
|
23
23
|
import Settings from '../../settings.js'
|
|
24
24
|
|
|
25
|
-
// temporary counter to work around shader caching issues
|
|
26
|
-
let counter = 0
|
|
27
|
-
|
|
28
25
|
/**
|
|
29
26
|
* Creates a padding object from a value and direction.
|
|
30
27
|
* @param {number|object|string|undefined} padding - The padding value.
|
|
@@ -90,10 +87,11 @@ const layoutFn = function (config) {
|
|
|
90
87
|
|
|
91
88
|
const children = this.node.children
|
|
92
89
|
const childrenLength = children.length
|
|
90
|
+
const elementChildren = this.children
|
|
93
91
|
let otherDimension = 0
|
|
94
92
|
const gap = config.gap || 0
|
|
95
93
|
for (let i = 0; i < childrenLength; i++) {
|
|
96
|
-
if (
|
|
94
|
+
if (elementChildren[i] !== undefined && elementChildren[i].props.raw.show === false) {
|
|
97
95
|
continue
|
|
98
96
|
}
|
|
99
97
|
const node = children[i]
|
|
@@ -686,9 +684,6 @@ const Element = {
|
|
|
686
684
|
this.config = null
|
|
687
685
|
delete this.config
|
|
688
686
|
|
|
689
|
-
this.counter = null
|
|
690
|
-
delete this.counter
|
|
691
|
-
|
|
692
687
|
this.props.raw = {}
|
|
693
688
|
this.props.element = null
|
|
694
689
|
this.props.props = null
|
|
@@ -714,9 +709,20 @@ const Element = {
|
|
|
714
709
|
return this.node && this.node.parent
|
|
715
710
|
},
|
|
716
711
|
get children() {
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
712
|
+
const allChildren = this.component[symbols.getChildren]()
|
|
713
|
+
const directChildren = []
|
|
714
|
+
const l = allChildren.length
|
|
715
|
+
for (let i = 0; i < l; i++) {
|
|
716
|
+
const child = allChildren[i]
|
|
717
|
+
if (
|
|
718
|
+
child !== undefined &&
|
|
719
|
+
child.parent === (this[symbols.isSlot] ? this.node.children[0] : this.node)
|
|
720
|
+
) {
|
|
721
|
+
directChildren.push(child)
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
return directChildren
|
|
720
726
|
},
|
|
721
727
|
}
|
|
722
728
|
|
|
@@ -739,6 +745,5 @@ export default (config, component) => {
|
|
|
739
745
|
scheduledTransitions: {},
|
|
740
746
|
config,
|
|
741
747
|
component,
|
|
742
|
-
counter: counter++,
|
|
743
748
|
})
|
|
744
749
|
}
|
package/src/focus.js
CHANGED
|
@@ -48,7 +48,10 @@ export default {
|
|
|
48
48
|
focusedComponent = component
|
|
49
49
|
focusedComponent.lifecycle.state = 'focus'
|
|
50
50
|
if (event instanceof KeyboardEvent) {
|
|
51
|
-
|
|
51
|
+
const internalEvent = new KeyboardEvent('keydown', event)
|
|
52
|
+
// @ts-ignore - this is an internal event
|
|
53
|
+
internalEvent[symbols.internalEvent] = true
|
|
54
|
+
document.dispatchEvent(internalEvent)
|
|
52
55
|
} else {
|
|
53
56
|
focusChain = []
|
|
54
57
|
}
|
package/src/lib/symbols.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* @property {symbol} index
|
|
14
14
|
* @property {symbol} init
|
|
15
15
|
* @property {symbol} inputEvents
|
|
16
|
+
* @property {symbol} internalEvent
|
|
16
17
|
* @property {symbol} intervals
|
|
17
18
|
* @property {symbol} launched
|
|
18
19
|
* @property {symbol} level
|
|
@@ -62,6 +63,7 @@ export default {
|
|
|
62
63
|
index: Symbol('index'),
|
|
63
64
|
init: Symbol('init'),
|
|
64
65
|
inputEvents: Symbol('inputEvents'),
|
|
66
|
+
internalEvent: Symbol('internalEvent'),
|
|
65
67
|
intervals: Symbol('intervals'),
|
|
66
68
|
launched: Symbol('launched'),
|
|
67
69
|
level: Symbol('level'),
|
package/src/router/router.js
CHANGED
|
@@ -207,18 +207,29 @@ export const navigate = async function () {
|
|
|
207
207
|
const { hash, path, queryParams } = getHash()
|
|
208
208
|
let route = matchHash(path, this.parent[symbols.routes])
|
|
209
209
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
210
|
+
const queryParamsData = {}
|
|
211
|
+
const queryParamsEntries = [...queryParams.entries()]
|
|
212
|
+
for (let i = 0; i < queryParamsEntries.length; i++) {
|
|
213
|
+
queryParamsData[queryParamsEntries[i][0]] = queryParamsEntries[i][1]
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
route.data = {
|
|
217
|
+
...navigationData,
|
|
218
|
+
...route.data,
|
|
219
|
+
...queryParamsData,
|
|
213
220
|
}
|
|
214
221
|
|
|
215
222
|
currentRoute = route
|
|
216
223
|
if (route) {
|
|
224
|
+
// Adding the location hash to the route if it exists.
|
|
225
|
+
if (hash !== null) {
|
|
226
|
+
route.hash = hash
|
|
227
|
+
}
|
|
217
228
|
let beforeEachResult
|
|
218
229
|
if (this.parent[symbols.routerHooks]) {
|
|
219
230
|
const hooks = this.parent[symbols.routerHooks]
|
|
220
231
|
if (hooks.beforeEach) {
|
|
221
|
-
beforeEachResult = await hooks.beforeEach(route, previousRoute)
|
|
232
|
+
beforeEachResult = await hooks.beforeEach.call(this.parent, route, previousRoute)
|
|
222
233
|
if (isString(beforeEachResult)) {
|
|
223
234
|
to(beforeEachResult)
|
|
224
235
|
return
|
|
@@ -258,7 +269,6 @@ export const navigate = async function () {
|
|
|
258
269
|
|
|
259
270
|
/** @type {import('../engines/L3/element.js').BlitsElement} */
|
|
260
271
|
let holder
|
|
261
|
-
let routeData
|
|
262
272
|
|
|
263
273
|
let { view, focus } = cacheMap.get(route.hash) || {}
|
|
264
274
|
|
|
@@ -279,23 +289,11 @@ export const navigate = async function () {
|
|
|
279
289
|
holder.set('w', '100%')
|
|
280
290
|
holder.set('h', '100%')
|
|
281
291
|
|
|
282
|
-
const queryParamsData = {}
|
|
283
|
-
const queryParamsEntries = [...queryParams.entries()]
|
|
284
|
-
for (let i = 0; i < queryParamsEntries.length; i++) {
|
|
285
|
-
queryParamsData[queryParamsEntries[i][0]] = queryParamsEntries[i][1]
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
routeData = {
|
|
289
|
-
...navigationData,
|
|
290
|
-
...route.data,
|
|
291
|
-
...queryParamsData,
|
|
292
|
-
}
|
|
293
|
-
|
|
294
292
|
// merge props with potential route params, navigation data and route data to be injected into the component instance
|
|
295
293
|
const props = {
|
|
296
294
|
...this[symbols.props],
|
|
297
295
|
...route.params,
|
|
298
|
-
...
|
|
296
|
+
...route.data,
|
|
299
297
|
}
|
|
300
298
|
|
|
301
299
|
view = await route.component({ props }, holder, this)
|
|
@@ -369,7 +367,7 @@ export const navigate = async function () {
|
|
|
369
367
|
state.path = route.path
|
|
370
368
|
state.params = route.params
|
|
371
369
|
state.hash = hash
|
|
372
|
-
state.data =
|
|
370
|
+
state.data = route.data
|
|
373
371
|
|
|
374
372
|
// apply in transition
|
|
375
373
|
if (route.transition.in) {
|
|
@@ -383,10 +381,14 @@ export const navigate = async function () {
|
|
|
383
381
|
await setOrAnimate(holder, route.transition.in, shouldAnimate)
|
|
384
382
|
}
|
|
385
383
|
}
|
|
386
|
-
|
|
387
|
-
this.activeView =
|
|
384
|
+
const children = this[symbols.children]
|
|
385
|
+
this.activeView = children[children.length - 1]
|
|
388
386
|
} else {
|
|
389
387
|
Log.error(`Route ${hash} not found`)
|
|
388
|
+
const routerHooks = this.parent[symbols.routerHooks]
|
|
389
|
+
if (routerHooks && typeof routerHooks.error === 'function') {
|
|
390
|
+
routerHooks.error.call(this.parent, `Route ${hash} not found`)
|
|
391
|
+
}
|
|
390
392
|
}
|
|
391
393
|
}
|
|
392
394
|
|