@lightningjs/blits 2.0.1 → 2.1.1
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 +19 -1
- package/index.d.ts +7 -0
- package/package.json +1 -1
- package/src/application.js +2 -2
- package/src/component/base/methods.js +3 -3
- package/src/component/base/timeouts_intervals.js +10 -0
- package/src/engines/L3/element.js +16 -5
- package/src/focus/focus.js +3 -4
- package/src/lib/codegenerator/generator.js +25 -5
- package/src/lib/events/internalkeyboardevent.js +29 -0
- package/src/lib/utils.js +9 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v2.1.1
|
|
4
|
+
|
|
5
|
+
_9 apr 2026_
|
|
6
|
+
|
|
7
|
+
- Fixed import file extension issue
|
|
8
|
+
|
|
9
|
+
## v2.1.0
|
|
10
|
+
|
|
11
|
+
_9 apr 2026_
|
|
12
|
+
|
|
13
|
+
- Fixed left over `KeyboardEvent.code` references
|
|
14
|
+
- Fixed issue with allowing $-prefix to be used as text content
|
|
15
|
+
- Fixed issue with internal keyboard event not working on older browsers
|
|
16
|
+
- Added tests to router hooks
|
|
17
|
+
- Fixed issue with transitions not being skipped when duration is set to `0`
|
|
18
|
+
- Added `this.$nextTick` utility helper
|
|
19
|
+
- Fixed issue with transitioning colours in `border`-attribute
|
|
20
|
+
|
|
3
21
|
## v2.0.1
|
|
4
22
|
|
|
5
23
|
_23 mar 2026_
|
|
@@ -7,7 +25,7 @@ _23 mar 2026_
|
|
|
7
25
|
- Fixed issue with height dimension of router view
|
|
8
26
|
- Fixed issue with array or rounded corners not being reactive
|
|
9
27
|
- Fixed issue with font not being mapped correctly when using `png` in the font config
|
|
10
|
-
- Removed
|
|
28
|
+
- Removed obsolete key config
|
|
11
29
|
- Fixed reactivity guard in computed props to handle deep nested references more safely
|
|
12
30
|
|
|
13
31
|
## v2.0.0
|
package/index.d.ts
CHANGED
|
@@ -474,6 +474,13 @@ declare module '@lightningjs/blits' {
|
|
|
474
474
|
*/
|
|
475
475
|
$clearDebounces: () => void
|
|
476
476
|
|
|
477
|
+
/**
|
|
478
|
+
* Defer a callback to the next tick (setTimeout 0), automatically cleaned upon component destroy
|
|
479
|
+
* @param callback - Function to execute on the next tick
|
|
480
|
+
* @param args - Arguments to pass to the callback
|
|
481
|
+
*/
|
|
482
|
+
$nextTick: (callback: (...args: any[]) => void, ...args: any[]) => ReturnType<typeof setTimeout>
|
|
483
|
+
|
|
477
484
|
/**
|
|
478
485
|
* Set an interval that is automatically cleaned upon component destroy
|
|
479
486
|
*/
|
package/package.json
CHANGED
package/src/application.js
CHANGED
|
@@ -118,9 +118,9 @@ const Application = (config) => {
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
keyUpHandler = (e) => {
|
|
121
|
-
const cb = keyUpCallbacks.get(e.
|
|
121
|
+
const cb = keyUpCallbacks.get(e.keyCode)
|
|
122
122
|
if (cb !== undefined && typeof cb === 'function') {
|
|
123
|
-
keyUpCallbacks.delete(e.
|
|
123
|
+
keyUpCallbacks.delete(e.keyCode)
|
|
124
124
|
cb()
|
|
125
125
|
}
|
|
126
126
|
clearTimeout(holdTimeout)
|
|
@@ -51,7 +51,7 @@ export default {
|
|
|
51
51
|
if (event === null || event === undefined || event instanceof KeyboardEvent === false)
|
|
52
52
|
return false
|
|
53
53
|
|
|
54
|
-
const key = keyMap[event.
|
|
54
|
+
const key = keyMap[event.keyCode] || event.keyCode
|
|
55
55
|
|
|
56
56
|
const componentWithInputEvent = getComponentWithInputEvent(this, key)
|
|
57
57
|
if (componentWithInputEvent === null) return false
|
|
@@ -65,8 +65,8 @@ export default {
|
|
|
65
65
|
cb = inputEvents.any.call(componentWithInputEvent, event)
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
if (cb !== undefined && event.
|
|
69
|
-
keyUpCallbacks.set(event.
|
|
68
|
+
if (cb !== undefined && event.keyCode) {
|
|
69
|
+
keyUpCallbacks.set(event.keyCode, cb)
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
return true
|
|
@@ -152,4 +152,14 @@ export default {
|
|
|
152
152
|
enumerable: true,
|
|
153
153
|
configurable: false,
|
|
154
154
|
},
|
|
155
|
+
$nextTick: {
|
|
156
|
+
value: function (fn, ...params) {
|
|
157
|
+
// early exit when component is marked as end of life
|
|
158
|
+
if (this.eol === true) return
|
|
159
|
+
return this.$setTimeout(fn, 0, ...params)
|
|
160
|
+
},
|
|
161
|
+
writable: false,
|
|
162
|
+
enumerable: true,
|
|
163
|
+
configurable: false,
|
|
164
|
+
},
|
|
155
165
|
}
|
|
@@ -16,7 +16,13 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { renderer } from './launch.js'
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
parseToObject,
|
|
21
|
+
isObjectString,
|
|
22
|
+
isArrayString,
|
|
23
|
+
isTransition,
|
|
24
|
+
isZeroDurationTransition,
|
|
25
|
+
} from '../../lib/utils.js'
|
|
20
26
|
import colors from '../../lib/colors/colors.js'
|
|
21
27
|
|
|
22
28
|
import { Log } from '../../lib/log.js'
|
|
@@ -377,14 +383,17 @@ const propsTransformer = {
|
|
|
377
383
|
},
|
|
378
384
|
set border(v) {
|
|
379
385
|
this.props['border'] = v
|
|
386
|
+
|
|
380
387
|
if (
|
|
381
388
|
this.element.node !== undefined &&
|
|
382
389
|
this.elementShader === true &&
|
|
383
390
|
(typeof v === 'object' || isObjectString(v) === true)
|
|
384
391
|
) {
|
|
385
392
|
v = shaders.parseProps(v)
|
|
393
|
+
const shader = this.element.node.props['shader']
|
|
394
|
+
let prefix = shader.shaderKey.startsWith('rounded') ? 'border-' : ''
|
|
386
395
|
for (const key in v) {
|
|
387
|
-
this.element.node.props['shader'].props[
|
|
396
|
+
this.element.node.props['shader'].props[prefix + key] = v[key]
|
|
388
397
|
}
|
|
389
398
|
}
|
|
390
399
|
},
|
|
@@ -396,8 +405,10 @@ const propsTransformer = {
|
|
|
396
405
|
(typeof v === 'object' || isObjectString(v) === true)
|
|
397
406
|
) {
|
|
398
407
|
v = shaders.parseProps(v)
|
|
408
|
+
const shader = this.element.node.props['shader']
|
|
409
|
+
let prefix = shader.shaderKey.startsWith('rounded') ? 'shadow-' : ''
|
|
399
410
|
for (const key in v) {
|
|
400
|
-
this.element.node.props['shader'].props[
|
|
411
|
+
this.element.node.props['shader'].props[prefix + key] = v[key]
|
|
401
412
|
}
|
|
402
413
|
}
|
|
403
414
|
},
|
|
@@ -663,7 +674,7 @@ const Element = {
|
|
|
663
674
|
const propsKeys = Object.keys(this.props.props)
|
|
664
675
|
|
|
665
676
|
if (propsKeys.length === 1) {
|
|
666
|
-
if (isTransition(value) === true) {
|
|
677
|
+
if (isTransition(value) === true && isZeroDurationTransition(value) === false) {
|
|
667
678
|
return this.animate(propsKeys[0], this.props.props[propsKeys[0]], value.transition)
|
|
668
679
|
}
|
|
669
680
|
// set the prop to the value on the node
|
|
@@ -671,7 +682,7 @@ const Element = {
|
|
|
671
682
|
} else {
|
|
672
683
|
for (let i = 0; i < propsKeys.length; i++) {
|
|
673
684
|
// todo: fix code duplication
|
|
674
|
-
if (isTransition(value) === true) {
|
|
685
|
+
if (isTransition(value) === true && isZeroDurationTransition(value) === false) {
|
|
675
686
|
this.animate(propsKeys[i], this.props.props[propsKeys[i]], value.transition)
|
|
676
687
|
} else {
|
|
677
688
|
// set the prop to the value on the node
|
package/src/focus/focus.js
CHANGED
|
@@ -21,6 +21,7 @@ import Settings from '../settings.js'
|
|
|
21
21
|
import { DEFAULT_HOLD_TIMEOUT_MS } from '../constants.js'
|
|
22
22
|
import { Log } from '../lib/log.js'
|
|
23
23
|
import { getAncestors } from './helpers.js'
|
|
24
|
+
import InternalKeyboardEvent from '../lib/events/internalkeyboardevent.js'
|
|
24
25
|
|
|
25
26
|
let focusedComponent = null
|
|
26
27
|
let focusChain = []
|
|
@@ -95,7 +96,7 @@ export default {
|
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
if (cb !== undefined) {
|
|
98
|
-
keyUpCallbacks.set(event.
|
|
99
|
+
keyUpCallbacks.set(event.keyCode, cb)
|
|
99
100
|
}
|
|
100
101
|
},
|
|
101
102
|
}
|
|
@@ -133,9 +134,7 @@ const setFocus = (component, event) => {
|
|
|
133
134
|
component[symbols.lifecycle].state = 'focus'
|
|
134
135
|
|
|
135
136
|
if (event instanceof KeyboardEvent) {
|
|
136
|
-
const internalEvent = new
|
|
137
|
-
// @ts-ignore - this is an internal event
|
|
138
|
-
internalEvent[symbols.internalEvent] = true
|
|
137
|
+
const internalEvent = new InternalKeyboardEvent('keydown', event)
|
|
139
138
|
document.dispatchEvent(internalEvent)
|
|
140
139
|
}
|
|
141
140
|
}
|
|
@@ -116,7 +116,27 @@ export default function (templateObject = { children: [] }, devMode = false) {
|
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
// This is used to get only variable from expression
|
|
119
|
-
const extractVariables = function (value) {
|
|
119
|
+
const extractVariables = function (value, isContentKey = false) {
|
|
120
|
+
if (isContentKey) {
|
|
121
|
+
// Strip string literals (single and double quoted) so that $variables
|
|
122
|
+
// embedded in plain text (e.g. "'this.$notifications.add()'") are not
|
|
123
|
+
// mistakenly extracted as component variable references.
|
|
124
|
+
const stripped = value.replace(/'[^']*'|"[^"]*"/g, '')
|
|
125
|
+
|
|
126
|
+
// A $variable is a real component reference only if:
|
|
127
|
+
// 1. The value starts with a bare $variable (e.g. "$notification.add(ms,)")
|
|
128
|
+
// 2. The stripped text contains string concatenation (e.g. "'Count: ' + $notifications.count")
|
|
129
|
+
// Plain text with embedded $variables (e.g. "Custom plugin with $reactive state")
|
|
130
|
+
// should not trigger verification.
|
|
131
|
+
const isBareDollarVar = /^\s*\$\$?\w+/.test(stripped)
|
|
132
|
+
const hasConcatExpression = /\+/.test(stripped)
|
|
133
|
+
|
|
134
|
+
if (!isBareDollarVar && !hasConcatExpression) {
|
|
135
|
+
return false
|
|
136
|
+
}
|
|
137
|
+
value = stripped
|
|
138
|
+
}
|
|
139
|
+
|
|
120
140
|
const regEx = /\$\$?\w+(\.\w+)?/g
|
|
121
141
|
const matches = value.match(regEx)
|
|
122
142
|
if (matches !== null) {
|
|
@@ -127,8 +147,8 @@ const extractVariables = function (value) {
|
|
|
127
147
|
}
|
|
128
148
|
}
|
|
129
149
|
|
|
130
|
-
const verifyVariables = function (value, renderCode, type = 'dynamic') {
|
|
131
|
-
const variablesToBeVerified = extractVariables(value)
|
|
150
|
+
const verifyVariables = function (value, renderCode, isContentKey = false, type = 'dynamic') {
|
|
151
|
+
const variablesToBeVerified = extractVariables(value, isContentKey)
|
|
132
152
|
if (variablesToBeVerified !== false) {
|
|
133
153
|
for (let i = 0; i < variablesToBeVerified.length; i++) {
|
|
134
154
|
let variable = variablesToBeVerified[i]
|
|
@@ -222,7 +242,7 @@ const generateElementCode = function (
|
|
|
222
242
|
}
|
|
223
243
|
// value.includes('.') === false &&
|
|
224
244
|
if (isDev === true && options.component !== 'scope.' && value.includes('$')) {
|
|
225
|
-
verifyVariables(value, renderCode, 'reactive')
|
|
245
|
+
verifyVariables(value, renderCode, key === ':content', 'reactive')
|
|
226
246
|
}
|
|
227
247
|
renderCode.push(
|
|
228
248
|
`elementConfigs[${counter}]['${key.substring(1)}'] = ${interpolate(
|
|
@@ -232,7 +252,7 @@ const generateElementCode = function (
|
|
|
232
252
|
)
|
|
233
253
|
} else {
|
|
234
254
|
if (isDev === true && options.component !== 'scope.' && value.includes('$')) {
|
|
235
|
-
verifyVariables(value, renderCode)
|
|
255
|
+
verifyVariables(value, renderCode, key === 'content')
|
|
236
256
|
}
|
|
237
257
|
renderCode.push(
|
|
238
258
|
`elementConfigs[${counter}]['${key}'] = ${cast(value, key, options.component)}`
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import symbols from '../symbols.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Internal keyboard event class that extends the native KeyboardEvent.
|
|
5
|
+
* This allows us to set props on the event from the eventInitDict,
|
|
6
|
+
* which is not correctly implemented on some browsers.
|
|
7
|
+
*
|
|
8
|
+
* @extends {KeyboardEvent}
|
|
9
|
+
*/
|
|
10
|
+
export default class InternalKeyboardEvent extends KeyboardEvent {
|
|
11
|
+
[symbols.internalEvent] = true
|
|
12
|
+
_keyCode = 0
|
|
13
|
+
|
|
14
|
+
get keyCode() {
|
|
15
|
+
return this._keyCode
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {string} type - A string with the name of the event
|
|
20
|
+
* @param {KeyboardEventInit} [eventInitDict] - An object that configures the event
|
|
21
|
+
*/
|
|
22
|
+
constructor(type, eventInitDict) {
|
|
23
|
+
super(type, eventInitDict)
|
|
24
|
+
|
|
25
|
+
if ('keyCode' in eventInitDict) {
|
|
26
|
+
this._keyCode = eventInitDict.keyCode
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
package/src/lib/utils.js
CHANGED
|
@@ -52,6 +52,15 @@ export const isTransition = (value) => {
|
|
|
52
52
|
return value !== null && typeof value === 'object' && 'transition' in value === true
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Checks if a transition object has a duration of zero.
|
|
57
|
+
* @param {any} value - The value to check.
|
|
58
|
+
* @returns {boolean} True if the transition duration is zero, false otherwise.
|
|
59
|
+
*/
|
|
60
|
+
export const isZeroDurationTransition = (value) => {
|
|
61
|
+
return value.transition.duration === 0
|
|
62
|
+
}
|
|
63
|
+
|
|
55
64
|
/**
|
|
56
65
|
* Checks if a string is an object string (starts and ends with curly braces).
|
|
57
66
|
* @param {string} str - The string to check.
|