@lightningjs/blits 1.43.1 → 1.44.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 +16 -1
- package/index.d.ts +6 -0
- package/package.json +2 -2
- package/src/application.js +10 -21
- package/src/component/base/methods.js +37 -0
- package/src/constants.js +19 -0
- package/src/engines/L3/element.js +8 -2
- package/src/focus.js +1 -1
- package/src/router/router.js +18 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v1.44.0
|
|
4
|
+
|
|
5
|
+
_20 Nov 2025_
|
|
6
|
+
|
|
7
|
+
- Added `$input` method to facilitate key handling _without_ also passing focus
|
|
8
|
+
- Added support for passing `type` of image in the `src`-attribute (`regular`, `svg`, `compressed` for cases the file type can't be derived from the file name)
|
|
9
|
+
- Bumped Lightning renderer to v2.20.0 with improved support for compressed textures
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## v1.43.2
|
|
13
|
+
|
|
14
|
+
_13 Nov 2025_
|
|
15
|
+
|
|
16
|
+
- Fixed issue with endless loop in router when first page in history would fail an async before / beforeEach hook (introduced in v1.43.0)
|
|
17
|
+
|
|
3
18
|
## v1.43.1
|
|
4
19
|
|
|
5
|
-
|
|
20
|
+
_13 Nov 2025_
|
|
6
21
|
|
|
7
22
|
- Added arguments of fpsUpdate and frameTick hooks to type definitions
|
|
8
23
|
|
package/index.d.ts
CHANGED
|
@@ -431,6 +431,12 @@ declare module '@lightningjs/blits' {
|
|
|
431
431
|
* Set focus to the Component, optionally pass a KeyboardEvent for instant event bubbling
|
|
432
432
|
*/
|
|
433
433
|
$focus: (event?: KeyboardEvent) => void
|
|
434
|
+
/**
|
|
435
|
+
* Handle a keyboard event on this component without changing focus
|
|
436
|
+
* @param event - The keyboard event to handle
|
|
437
|
+
* @returns Returns true if this component or a parent component handled the event, false otherwise
|
|
438
|
+
*/
|
|
439
|
+
$input: (event: KeyboardEvent) => boolean
|
|
434
440
|
/**
|
|
435
441
|
* @deprecated
|
|
436
442
|
* Deprecated: use `this.$focus()` instead
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightningjs/blits",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.44.0",
|
|
4
4
|
"description": "Blits: The Lightning 3 App Development Framework",
|
|
5
5
|
"bin": "bin/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@lightningjs/msdf-generator": "^1.2.0",
|
|
58
|
-
"@lightningjs/renderer": "^2.
|
|
58
|
+
"@lightningjs/renderer": "^2.20.0"
|
|
59
59
|
},
|
|
60
60
|
"repository": {
|
|
61
61
|
"type": "git",
|
package/src/application.js
CHANGED
|
@@ -20,28 +20,16 @@ import { default as Focus, keyUpCallbacks } from './focus.js'
|
|
|
20
20
|
import Settings from './settings.js'
|
|
21
21
|
|
|
22
22
|
import symbols from './lib/symbols.js'
|
|
23
|
-
import { DEFAULT_HOLD_TIMEOUT_MS } from './constants.js'
|
|
23
|
+
import { DEFAULT_HOLD_TIMEOUT_MS, DEFAULT_KEYMAP } from './constants.js'
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Enter: 'enter',
|
|
32
|
-
' ': 'space',
|
|
33
|
-
Backspace: 'back',
|
|
34
|
-
Escape: 'escape',
|
|
35
|
-
37: 'left',
|
|
36
|
-
39: 'right',
|
|
37
|
-
38: 'up',
|
|
38
|
-
40: 'down',
|
|
39
|
-
13: 'enter',
|
|
40
|
-
32: 'space',
|
|
41
|
-
8: 'back',
|
|
42
|
-
27: 'escape',
|
|
43
|
-
}
|
|
25
|
+
/**
|
|
26
|
+
* Merged keyMap (default + custom settings).
|
|
27
|
+
* Initialized once during application init.
|
|
28
|
+
* @type {Object<string, string>}
|
|
29
|
+
*/
|
|
30
|
+
export let keyMap = {}
|
|
44
31
|
|
|
32
|
+
const Application = (config) => {
|
|
45
33
|
config.hooks = config.hooks || {}
|
|
46
34
|
|
|
47
35
|
let keyDownHandler
|
|
@@ -63,7 +51,8 @@ const Application = (config) => {
|
|
|
63
51
|
if (announcerOptions && typeof announcerOptions === 'object') {
|
|
64
52
|
this.$announcer.configure(announcerOptions)
|
|
65
53
|
}
|
|
66
|
-
|
|
54
|
+
// Initialize merged keyMap once during application init
|
|
55
|
+
keyMap = { ...DEFAULT_KEYMAP, ...Settings.get('keymap', {}) }
|
|
67
56
|
|
|
68
57
|
/** @type {number} Input throttle time in milliseconds (0 = disabled) */
|
|
69
58
|
const throttleMs = Settings.get('inputThrottle', 0)
|
|
@@ -17,11 +17,13 @@
|
|
|
17
17
|
|
|
18
18
|
import symbols from '../../lib/symbols.js'
|
|
19
19
|
import Focus from '../../focus.js'
|
|
20
|
+
import { keyUpCallbacks, getComponentWithInputEvent } from '../../focus.js'
|
|
20
21
|
import eventListeners from '../../lib/eventListeners.js'
|
|
21
22
|
import { trigger } from '../../lib/reactivity/effect.js'
|
|
22
23
|
import { Log } from '../../lib/log.js'
|
|
23
24
|
import { removeGlobalEffects } from '../../lib/reactivity/effect.js'
|
|
24
25
|
import { renderer } from '../../launch.js'
|
|
26
|
+
import { keyMap } from '../../application.js'
|
|
25
27
|
|
|
26
28
|
export default {
|
|
27
29
|
focus: {
|
|
@@ -51,6 +53,41 @@ export default {
|
|
|
51
53
|
enumerable: true,
|
|
52
54
|
configurable: false,
|
|
53
55
|
},
|
|
56
|
+
$input: {
|
|
57
|
+
/**
|
|
58
|
+
* Handle a keyboard event on this component without changing focus
|
|
59
|
+
* @this {import('../../component').BlitsComponent}
|
|
60
|
+
* @param {KeyboardEvent} event - The keyboard event to handle
|
|
61
|
+
* @returns {boolean} - Returns true if this component or a parent component handled the event, false otherwise
|
|
62
|
+
*/
|
|
63
|
+
value: function (event) {
|
|
64
|
+
if (event === null || event === undefined || event instanceof KeyboardEvent === false)
|
|
65
|
+
return false
|
|
66
|
+
|
|
67
|
+
const key = keyMap[event.key] || keyMap[event.keyCode] || event.key || event.keyCode
|
|
68
|
+
|
|
69
|
+
const componentWithInputEvent = getComponentWithInputEvent(this, key)
|
|
70
|
+
if (componentWithInputEvent === null) return false
|
|
71
|
+
|
|
72
|
+
const inputEvents = componentWithInputEvent[symbols.inputEvents] || {}
|
|
73
|
+
|
|
74
|
+
let cb
|
|
75
|
+
if (inputEvents[key]) {
|
|
76
|
+
cb = inputEvents[key].call(componentWithInputEvent, event)
|
|
77
|
+
} else if (inputEvents.any) {
|
|
78
|
+
cb = inputEvents.any.call(componentWithInputEvent, event)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (cb !== undefined && event.code) {
|
|
82
|
+
keyUpCallbacks.set(event.code, cb)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return true
|
|
86
|
+
},
|
|
87
|
+
writable: false,
|
|
88
|
+
enumerable: true,
|
|
89
|
+
configurable: false,
|
|
90
|
+
},
|
|
54
91
|
unfocus: {
|
|
55
92
|
/**
|
|
56
93
|
* @this {import('../../component').BlitsComponent}
|
package/src/constants.js
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
export const DEFAULT_HOLD_TIMEOUT_MS = 50
|
|
2
2
|
|
|
3
|
+
export const DEFAULT_KEYMAP = {
|
|
4
|
+
ArrowLeft: 'left',
|
|
5
|
+
ArrowRight: 'right',
|
|
6
|
+
ArrowUp: 'up',
|
|
7
|
+
ArrowDown: 'down',
|
|
8
|
+
Enter: 'enter',
|
|
9
|
+
' ': 'space',
|
|
10
|
+
Backspace: 'back',
|
|
11
|
+
Escape: 'escape',
|
|
12
|
+
37: 'left',
|
|
13
|
+
39: 'right',
|
|
14
|
+
38: 'up',
|
|
15
|
+
40: 'down',
|
|
16
|
+
13: 'enter',
|
|
17
|
+
32: 'space',
|
|
18
|
+
8: 'back',
|
|
19
|
+
27: 'escape',
|
|
20
|
+
}
|
|
21
|
+
|
|
3
22
|
export const SCREEN_RESOLUTIONS = {
|
|
4
23
|
hd: 0.66666667,
|
|
5
24
|
'720p': 0.66666667,
|
|
@@ -168,7 +168,7 @@ const isObjectString = (str) => {
|
|
|
168
168
|
* @returns {object} The parsed object.
|
|
169
169
|
*/
|
|
170
170
|
const parseToObject = (str) => {
|
|
171
|
-
return JSON.parse(str.replace(/'/g, '"').replace(/([\w-_]+)\s
|
|
171
|
+
return JSON.parse(str.replace(/'/g, '"').replace(/([{,]\s*)([\w-_]+)(\s*:)/g, '$1"$2"$3'))
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
/**
|
|
@@ -276,7 +276,13 @@ const propsTransformer = {
|
|
|
276
276
|
}
|
|
277
277
|
},
|
|
278
278
|
set src(v) {
|
|
279
|
-
|
|
279
|
+
if (typeof v === 'object' || (isObjectString(v) === true && (v = parseToObject(v)))) {
|
|
280
|
+
this.props['src'] = v.src
|
|
281
|
+
this.props['imageType'] = v.type
|
|
282
|
+
} else {
|
|
283
|
+
this.props['src'] = v
|
|
284
|
+
}
|
|
285
|
+
|
|
280
286
|
if (this.raw['color'] === undefined) {
|
|
281
287
|
this.props['color'] = this.props['src'] ? 0xffffffff : 0x00000000
|
|
282
288
|
}
|
package/src/focus.js
CHANGED
|
@@ -118,7 +118,7 @@ const getAncestors = (components) => {
|
|
|
118
118
|
* @param {String} key
|
|
119
119
|
* @returns component
|
|
120
120
|
*/
|
|
121
|
-
const getComponentWithInputEvent = (component, key) => {
|
|
121
|
+
export const getComponentWithInputEvent = (component, key) => {
|
|
122
122
|
if (
|
|
123
123
|
component[symbols.inputEvents] &&
|
|
124
124
|
(typeof component[symbols.inputEvents][key] === 'function' ||
|
package/src/router/router.js
CHANGED
|
@@ -267,13 +267,15 @@ export const navigate = async function () {
|
|
|
267
267
|
} catch (error) {
|
|
268
268
|
Log.error('Error or Rejected Promise in "BeforeEach" Hook', error)
|
|
269
269
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
270
|
+
if (history.length > 0) {
|
|
271
|
+
preventHashChangeNavigation = true
|
|
272
|
+
currentRoute = previousRoute
|
|
273
|
+
window.history.back()
|
|
273
274
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
275
|
+
navigatingBack = false
|
|
276
|
+
state.navigating = false
|
|
277
|
+
return
|
|
278
|
+
}
|
|
277
279
|
}
|
|
278
280
|
// If the resolved result is an object, redirect if the path in the object was changed
|
|
279
281
|
if (isObject(beforeEachResult) === true && beforeEachResult.path !== currentPath) {
|
|
@@ -282,7 +284,7 @@ export const navigate = async function () {
|
|
|
282
284
|
return
|
|
283
285
|
}
|
|
284
286
|
// If the resolved result is false, cancel navigation
|
|
285
|
-
if (beforeEachResult === false) {
|
|
287
|
+
if (beforeEachResult === false && history.length > 0) {
|
|
286
288
|
preventHashChangeNavigation = true
|
|
287
289
|
currentRoute = previousRoute
|
|
288
290
|
window.history.back()
|
|
@@ -306,13 +308,15 @@ export const navigate = async function () {
|
|
|
306
308
|
} catch (error) {
|
|
307
309
|
Log.error('Error or Rejected Promise in "Before" Hook', error)
|
|
308
310
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
311
|
+
if (history.length > 0) {
|
|
312
|
+
preventHashChangeNavigation = true
|
|
313
|
+
currentRoute = previousRoute
|
|
314
|
+
window.history.back()
|
|
312
315
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
+
navigatingBack = false
|
|
317
|
+
state.navigating = false
|
|
318
|
+
return
|
|
319
|
+
}
|
|
316
320
|
}
|
|
317
321
|
// If the resolved result is an object, redirect if the path in the object was changed
|
|
318
322
|
if (isObject(beforeHookOutput) === true && beforeHookOutput.path !== currentPath) {
|
|
@@ -321,7 +325,7 @@ export const navigate = async function () {
|
|
|
321
325
|
return
|
|
322
326
|
}
|
|
323
327
|
// If the resolved result is false, cancel navigation
|
|
324
|
-
if (beforeHookOutput === false) {
|
|
328
|
+
if (beforeHookOutput === false && history.length > 0) {
|
|
325
329
|
preventHashChangeNavigation = true
|
|
326
330
|
currentRoute = previousRoute
|
|
327
331
|
window.history.back()
|