@lightningjs/blits 2.9.0 → 2.11.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 +27 -0
- package/index.d.ts +19 -8
- package/package.json +19 -15
- package/src/application.js +13 -6
- package/src/component/base/methods.js +58 -15
- package/src/engines/L3/element.js +28 -8
- package/src/focus/focus.js +7 -1
- package/src/helpers/fetchJson.js +10 -2
- package/src/lib/codegenerator/generator.js +6 -0
- package/src/lib/colors/colors.js +3 -1
- package/src/lib/colors/htmlColors.d.ts +4 -0
- package/src/lib/reactivity/effect.js +20 -10
- package/src/lib/reactivity/reactive.js +16 -2
- package/src/lib/symbols.js +2 -0
- package/src/plugins/language.js +2 -1
- package/src/plugins/storage/storage.js +2 -2
- package/src/plugins/theme.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.11.0
|
|
4
|
+
|
|
5
|
+
_28 sep 2026_
|
|
6
|
+
|
|
7
|
+
- Improved `$select()` method by adding caching
|
|
8
|
+
- Upgraded several dependencies
|
|
9
|
+
- Bumped renderer to 3.5.1
|
|
10
|
+
|
|
11
|
+
## 2.10.0
|
|
12
|
+
|
|
13
|
+
_21 sep 2026_
|
|
14
|
+
|
|
15
|
+
- Fixed / improved type definitions
|
|
16
|
+
- Fixed issue with accidental sideeffect running when `$hasFocus` triggers after destroy
|
|
17
|
+
- Fixed issue where an error in an effect can break execution of other effects
|
|
18
|
+
- Added support for reactivity of `array.length`
|
|
19
|
+
- Improved reactivity in the case of nested array operations
|
|
20
|
+
- Fixed case where transition callbacks could run after destroy
|
|
21
|
+
- Fixed case where keyup callback could fire after component destroy
|
|
22
|
+
- Improved error handling and parse rejection for fetchJson method
|
|
23
|
+
- Fixed typing of `fpsUpdate`
|
|
24
|
+
- Added textCalculated listener to trigger layout changes
|
|
25
|
+
- Added `mouseMoveThrottle` setting to configure responsiveness of mouse movements
|
|
26
|
+
- Fixed issue where hover is not recognized after keyboard use
|
|
27
|
+
- Added support for numeric rgba colors
|
|
28
|
+
- Bumped renderer to 3.5.0
|
|
29
|
+
|
|
3
30
|
## 2.9.0
|
|
4
31
|
|
|
5
32
|
_9 sep 2026_
|
package/index.d.ts
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
// blits file type reference
|
|
19
19
|
/// <reference path="./blits.d.ts" />
|
|
20
20
|
|
|
21
|
-
import {type ShaderEffect as RendererShaderEffect, type RendererMainSettings} from '@lightningjs/renderer'
|
|
21
|
+
import {type ShaderEffect as RendererShaderEffect, type RendererMainSettings, type FrameCounter} from '@lightningjs/renderer'
|
|
22
22
|
import { CanvasShaderType } from '@lightningjs/renderer/canvas';
|
|
23
23
|
import { WebGlShaderType } from '@lightningjs/renderer/webgl';
|
|
24
24
|
|
|
@@ -242,7 +242,7 @@ declare module '@lightningjs/blits' {
|
|
|
242
242
|
*
|
|
243
243
|
* Note: This event fire multiple times
|
|
244
244
|
*/
|
|
245
|
-
fpsUpdate?: (fps: number) => void;
|
|
245
|
+
fpsUpdate?: (fps: number, frameCounter: FrameCounter) => void;
|
|
246
246
|
}
|
|
247
247
|
|
|
248
248
|
export interface Input {
|
|
@@ -642,7 +642,9 @@ declare module '@lightningjs/blits' {
|
|
|
642
642
|
type InferProp<T> = T extends (...args: any[]) => any ? ReturnType<T> : T
|
|
643
643
|
|
|
644
644
|
type InferProps<T extends Record<string, any>> = {
|
|
645
|
-
|
|
645
|
+
// Since InferProps is no longer used for `Computed` props,
|
|
646
|
+
// then we do not need to infer the return type of a function-typed prop in `Props`.
|
|
647
|
+
[K in keyof T]: T[K]
|
|
646
648
|
}
|
|
647
649
|
|
|
648
650
|
type Computed<T extends Record<string, () => any>> = {
|
|
@@ -653,7 +655,7 @@ declare module '@lightningjs/blits' {
|
|
|
653
655
|
P extends Record<string, any>,
|
|
654
656
|
S,
|
|
655
657
|
M,
|
|
656
|
-
C extends Record<string, () => any>,
|
|
658
|
+
C extends Record<string, () => any> = Record<never, never>,
|
|
657
659
|
> = ThisType<
|
|
658
660
|
Readonly<InferProps<P>> & S & M & Computed<C> & ChildComponentBase
|
|
659
661
|
>
|
|
@@ -662,14 +664,14 @@ declare module '@lightningjs/blits' {
|
|
|
662
664
|
P extends Record<string, any>,
|
|
663
665
|
S,
|
|
664
666
|
M,
|
|
665
|
-
C,
|
|
667
|
+
C extends Record<string, () => any> = Record<never, never>,
|
|
666
668
|
> = ThisType<Readonly<InferProps<P>> & S & M & Computed<C> & ApplicationBase>
|
|
667
669
|
|
|
668
670
|
export interface ComponentConfig<
|
|
669
671
|
P extends Props = {},
|
|
670
672
|
S,
|
|
671
673
|
M,
|
|
672
|
-
C extends Record<string, () => any>,
|
|
674
|
+
C extends Record<string, () => any> = Record<never, never>,
|
|
673
675
|
W,
|
|
674
676
|
> {
|
|
675
677
|
components?: {
|
|
@@ -1408,6 +1410,14 @@ declare module '@lightningjs/blits' {
|
|
|
1408
1410
|
*/
|
|
1409
1411
|
enableMouse?: boolean,
|
|
1410
1412
|
/**
|
|
1413
|
+
* Minimum time in milliseconds between two pointer moves that are processed for hover.
|
|
1414
|
+
*
|
|
1415
|
+
* Moves arriving sooner are dropped, not delayed.
|
|
1416
|
+
*
|
|
1417
|
+
* @default 100
|
|
1418
|
+
*/
|
|
1419
|
+
mouseMoveThrottle?: number,
|
|
1420
|
+
/**
|
|
1411
1421
|
* Maximum FPS at which the App will be rendered
|
|
1412
1422
|
*
|
|
1413
1423
|
* Lowering the maximum FPS value can improve the overall experience on lower end devices.
|
|
@@ -1478,7 +1488,7 @@ declare module '@lightningjs/blits' {
|
|
|
1478
1488
|
P extends Props,
|
|
1479
1489
|
S extends State,
|
|
1480
1490
|
M extends Methods,
|
|
1481
|
-
C extends Computed,
|
|
1491
|
+
C extends Computed = Record<never, never>,
|
|
1482
1492
|
W extends Watch>(config: ApplicationConfig<P, S, M, C, W>) : ComponentFactory
|
|
1483
1493
|
/**
|
|
1484
1494
|
* Blits Component
|
|
@@ -1489,7 +1499,7 @@ declare module '@lightningjs/blits' {
|
|
|
1489
1499
|
P extends Props,
|
|
1490
1500
|
S extends State,
|
|
1491
1501
|
M extends Methods,
|
|
1492
|
-
C extends Computed,
|
|
1502
|
+
C extends Computed = Record<never, never>,
|
|
1493
1503
|
W extends Watch>(name: string, config: ComponentConfig<P, S, M, C, W>) : ComponentFactory
|
|
1494
1504
|
/**
|
|
1495
1505
|
* Blits Launch
|
|
@@ -1526,4 +1536,5 @@ declare module '@lightningjs/blits' {
|
|
|
1526
1536
|
const Blits: Blits;
|
|
1527
1537
|
|
|
1528
1538
|
export default Blits;
|
|
1539
|
+
|
|
1529
1540
|
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightningjs/blits",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.0",
|
|
4
4
|
"description": "Blits: The Lightning 3 App Development Framework",
|
|
5
5
|
"bin": "bin/index.js",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": "./index.js",
|
|
8
|
+
"./colors": {
|
|
9
|
+
"types": "./src/lib/colors/htmlColors.d.ts",
|
|
10
|
+
"default": "./src/lib/colors/htmlColors.js"
|
|
11
|
+
},
|
|
8
12
|
"./platforms": {
|
|
9
13
|
"types": "./src/platforms.d.ts",
|
|
10
14
|
"default": "./src/platforms.js"
|
|
@@ -70,29 +74,29 @@
|
|
|
70
74
|
"author": "Michiel van der Geest <hello@michielvandergeest.com>",
|
|
71
75
|
"license": "Apache-2.0",
|
|
72
76
|
"devDependencies": {
|
|
73
|
-
"@babel/eslint-parser": "^7.
|
|
74
|
-
"@babel/plugin-syntax-import-assertions": "^7.
|
|
75
|
-
"@eslint/eslintrc": "^3.3.
|
|
76
|
-
"@eslint/js": "^9.
|
|
77
|
-
"c8": "^
|
|
78
|
-
"eslint": "^9.
|
|
77
|
+
"@babel/eslint-parser": "^7.28.6",
|
|
78
|
+
"@babel/plugin-syntax-import-assertions": "^7.28.6",
|
|
79
|
+
"@eslint/eslintrc": "^3.3.4",
|
|
80
|
+
"@eslint/js": "^9.39.3",
|
|
81
|
+
"c8": "^12.0.0",
|
|
82
|
+
"eslint": "^9.39.3",
|
|
79
83
|
"eslint-config-prettier": "^10.1.8",
|
|
80
84
|
"eslint-plugin-prettier": "^5.5.4",
|
|
81
85
|
"fast-glob": "^3.3.3",
|
|
82
|
-
"global-jsdom": "
|
|
83
|
-
"globals": "^
|
|
86
|
+
"global-jsdom": "^30.0.0",
|
|
87
|
+
"globals": "^17.12.0",
|
|
84
88
|
"husky": "^9.1.7",
|
|
85
|
-
"jsdom": "
|
|
86
|
-
"lint-staged": "^15.5.
|
|
89
|
+
"jsdom": "^30.1.0",
|
|
90
|
+
"lint-staged": "^15.5.2",
|
|
87
91
|
"prettier": "^3.6.2",
|
|
88
|
-
"sinon": "^
|
|
92
|
+
"sinon": "^22.1.0",
|
|
89
93
|
"tap-diff": "^0.1.1",
|
|
90
94
|
"tap-filter": "^1.0.3",
|
|
91
|
-
"tape": "^5.
|
|
95
|
+
"tape": "^5.9.0"
|
|
92
96
|
},
|
|
93
97
|
"dependencies": {
|
|
94
|
-
"@lightningjs/renderer": "3.
|
|
95
|
-
"magic-string": "^
|
|
98
|
+
"@lightningjs/renderer": "3.5.1",
|
|
99
|
+
"magic-string": "^1.4.1"
|
|
96
100
|
},
|
|
97
101
|
"repository": {
|
|
98
102
|
"type": "git",
|
package/src/application.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* SPDX-License-Identifier: Apache-2.0
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import { default as Component
|
|
18
|
+
import { componentMap, default as Component } from './component.js'
|
|
19
19
|
import { default as Focus, keyUpCallbacks } from './focus/focus.js'
|
|
20
20
|
import Hover from './focus/hover.js'
|
|
21
21
|
|
|
@@ -80,6 +80,7 @@ const Application = (config) => {
|
|
|
80
80
|
const throttleMs = Settings.get('inputThrottle', 0)
|
|
81
81
|
|
|
82
82
|
const mouseEnabled = Settings.get('enableMouse', false)
|
|
83
|
+
const mouseMoveThrottle = Settings.get('mouseMoveThrottle', 100)
|
|
83
84
|
const { createKeyboardEvent, input, isKeyboardEvent, now = Date.now, viewport } = platform
|
|
84
85
|
|
|
85
86
|
keyDownHandler = async (e) => {
|
|
@@ -114,7 +115,11 @@ const Application = (config) => {
|
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
Focus.input(key, e)
|
|
117
|
-
if (mouseEnabled === true)
|
|
118
|
+
if (mouseEnabled === true) {
|
|
119
|
+
Hover.clear()
|
|
120
|
+
// forget the node under the pointer too, so moving on it hovers it again
|
|
121
|
+
currentNode = undefined
|
|
122
|
+
}
|
|
118
123
|
clearTimeout(holdTimeout)
|
|
119
124
|
holdTimeout = setTimeout(
|
|
120
125
|
() => {
|
|
@@ -125,10 +130,12 @@ const Application = (config) => {
|
|
|
125
130
|
}
|
|
126
131
|
|
|
127
132
|
keyUpHandler = (e) => {
|
|
128
|
-
const
|
|
129
|
-
if (
|
|
133
|
+
const entry = keyUpCallbacks.get(e.keyCode)
|
|
134
|
+
if (entry !== undefined) {
|
|
130
135
|
keyUpCallbacks.delete(e.keyCode)
|
|
131
|
-
|
|
136
|
+
if (entry.component.eol !== true && typeof entry.callback === 'function') {
|
|
137
|
+
entry.callback()
|
|
138
|
+
}
|
|
132
139
|
}
|
|
133
140
|
clearTimeout(holdTimeout)
|
|
134
141
|
Focus.hold = false
|
|
@@ -147,7 +154,7 @@ const Application = (config) => {
|
|
|
147
154
|
|
|
148
155
|
// limit the amount of move events per time frame
|
|
149
156
|
mouseMoveHandler = (e) => {
|
|
150
|
-
if (e.timeStamp - lastMoved <
|
|
157
|
+
if (e.timeStamp - lastMoved < mouseMoveThrottle) return
|
|
151
158
|
lastMoved = e.timeStamp
|
|
152
159
|
|
|
153
160
|
this.$emit('mouse::move', e)
|
|
@@ -16,7 +16,12 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import symbols from '../../lib/symbols.js'
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
default as Focus,
|
|
21
|
+
keyUpCallbacks,
|
|
22
|
+
getComponentWithInputEvent,
|
|
23
|
+
removeKeyUpCallbacks,
|
|
24
|
+
} from '../../focus/focus.js'
|
|
20
25
|
import eventListeners from '../../lib/eventListeners.js'
|
|
21
26
|
import { trigger } from '../../lib/reactivity/effect.js'
|
|
22
27
|
import { Log } from '../../lib/log.js'
|
|
@@ -25,6 +30,8 @@ import { renderer } from '../../launch.js'
|
|
|
25
30
|
import { keyMap } from '../../application.js'
|
|
26
31
|
import { platform } from '../../platform.js'
|
|
27
32
|
|
|
33
|
+
const selectCache = new WeakMap()
|
|
34
|
+
|
|
28
35
|
export default {
|
|
29
36
|
$focus: {
|
|
30
37
|
/**
|
|
@@ -71,7 +78,7 @@ export default {
|
|
|
71
78
|
}
|
|
72
79
|
|
|
73
80
|
if (cb !== undefined && event.keyCode) {
|
|
74
|
-
keyUpCallbacks.set(event.keyCode, cb)
|
|
81
|
+
keyUpCallbacks.set(event.keyCode, { callback: cb, component: componentWithInputEvent })
|
|
75
82
|
}
|
|
76
83
|
|
|
77
84
|
return true
|
|
@@ -98,14 +105,16 @@ export default {
|
|
|
98
105
|
*/
|
|
99
106
|
value: function () {
|
|
100
107
|
this.eol = true
|
|
108
|
+
selectCache.delete(this)
|
|
109
|
+
removeKeyUpCallbacks(this)
|
|
101
110
|
this[symbols.lifecycle].state = 'destroy'
|
|
102
111
|
|
|
112
|
+
removeEffects(this[symbols.effects])
|
|
113
|
+
|
|
103
114
|
// when destroying a component that currently has focus
|
|
104
115
|
// pass focus to the parent so we don't get lost in focus limbo
|
|
105
116
|
if (this.$hasFocus === true) this[symbols.parent].$focus()
|
|
106
117
|
|
|
107
|
-
removeEffects(this[symbols.effects])
|
|
108
|
-
|
|
109
118
|
// @todo - is this really necessary?
|
|
110
119
|
// This cause an issue with auto sizing of parent (and required an extra eol check there)
|
|
111
120
|
for (let key in this[symbols.state]) {
|
|
@@ -191,26 +200,60 @@ export default {
|
|
|
191
200
|
// early exit when component is marked as end of life
|
|
192
201
|
if (this.eol === true) return
|
|
193
202
|
|
|
194
|
-
let
|
|
195
|
-
|
|
203
|
+
let cache = selectCache.get(this)
|
|
204
|
+
if (cache !== undefined && cache.has(ref) === true) {
|
|
205
|
+
const selected = cache.get(ref)
|
|
206
|
+
if (selected !== undefined && selected.eol !== true) return selected
|
|
207
|
+
cache.delete(ref)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
for (const child of this[symbols.children]) {
|
|
211
|
+
if (child === undefined || child === null) continue
|
|
196
212
|
if (Array.isArray(child)) {
|
|
197
|
-
child.
|
|
198
|
-
|
|
199
|
-
|
|
213
|
+
const selected = child.find((c) => c['ref'] === ref)
|
|
214
|
+
if (selected !== undefined) {
|
|
215
|
+
if (cache === undefined) {
|
|
216
|
+
cache = new Map()
|
|
217
|
+
selectCache.set(this, cache)
|
|
218
|
+
}
|
|
219
|
+
cache.set(ref, selected)
|
|
220
|
+
return selected
|
|
221
|
+
}
|
|
200
222
|
} else if (Object.getPrototypeOf(child) === Object.prototype) {
|
|
201
|
-
Object.
|
|
202
|
-
|
|
203
|
-
|
|
223
|
+
const selected = Object.values(child).find((c) => c['ref'] === ref)
|
|
224
|
+
if (selected !== undefined) {
|
|
225
|
+
if (cache === undefined) {
|
|
226
|
+
cache = new Map()
|
|
227
|
+
selectCache.set(this, cache)
|
|
228
|
+
}
|
|
229
|
+
cache.set(ref, selected)
|
|
230
|
+
return selected
|
|
231
|
+
}
|
|
204
232
|
} else {
|
|
205
|
-
if (child['ref'] === ref)
|
|
233
|
+
if (child['ref'] === ref) {
|
|
234
|
+
if (cache === undefined) {
|
|
235
|
+
cache = new Map()
|
|
236
|
+
selectCache.set(this, cache)
|
|
237
|
+
}
|
|
238
|
+
cache.set(ref, child)
|
|
239
|
+
return child
|
|
240
|
+
}
|
|
206
241
|
}
|
|
207
|
-
}
|
|
208
|
-
return
|
|
242
|
+
}
|
|
243
|
+
return null
|
|
209
244
|
},
|
|
210
245
|
writable: false,
|
|
211
246
|
enumerable: true,
|
|
212
247
|
configurable: false,
|
|
213
248
|
},
|
|
249
|
+
[symbols.invalidateSelectCache]: {
|
|
250
|
+
value: function () {
|
|
251
|
+
selectCache.delete(this)
|
|
252
|
+
},
|
|
253
|
+
writable: false,
|
|
254
|
+
enumerable: false,
|
|
255
|
+
configurable: false,
|
|
256
|
+
},
|
|
214
257
|
$trigger: {
|
|
215
258
|
/**
|
|
216
259
|
* @this {import('../../component').BlitsComponent}
|
|
@@ -275,7 +275,9 @@ const propsTransformer = {
|
|
|
275
275
|
this.props['zIndex'] = v
|
|
276
276
|
},
|
|
277
277
|
set color(v) {
|
|
278
|
-
if (typeof v === '
|
|
278
|
+
if (typeof v === 'number') {
|
|
279
|
+
this.props['color'] = v
|
|
280
|
+
} else if (typeof v === 'string' && v.startsWith('{') === false) {
|
|
279
281
|
this.props['color'] = colors.normalize(v)
|
|
280
282
|
} else if (typeof v === 'object' || (isObjectString(v) === true && (v = parseToObject(v)))) {
|
|
281
283
|
this.props['color'] = 0
|
|
@@ -649,10 +651,11 @@ const Element = {
|
|
|
649
651
|
if (this.props.props['color'] === undefined && '__textnode' in props === false) {
|
|
650
652
|
this.props.props['color'] = 0
|
|
651
653
|
}
|
|
652
|
-
|
|
653
|
-
this.node =
|
|
654
|
-
|
|
655
|
-
|
|
654
|
+
const isTextNode = props.__textnode
|
|
655
|
+
this.node =
|
|
656
|
+
isTextNode === true
|
|
657
|
+
? renderer.createTextNode({ ...textDefaults, ...this.props.props })
|
|
658
|
+
: renderer.createNode(this.props.props)
|
|
656
659
|
|
|
657
660
|
if (this.props['holder'] === true) {
|
|
658
661
|
holderComponentMap.set(this.node, this.component)
|
|
@@ -680,7 +683,8 @@ const Element = {
|
|
|
680
683
|
|
|
681
684
|
if (this.config.parent.props !== undefined && this.config.parent.props.__layout === true) {
|
|
682
685
|
this.config.parent.triggerLayout(this.config.parent.props)
|
|
683
|
-
|
|
686
|
+
|
|
687
|
+
this.node.on(isTextNode === true ? 'textCalculated' : 'loaded', () => {
|
|
684
688
|
if (this.eol === true) return
|
|
685
689
|
this.config.parent.triggerLayout(this.config.parent.props)
|
|
686
690
|
})
|
|
@@ -809,6 +813,20 @@ const Element = {
|
|
|
809
813
|
// @ts-ignore
|
|
810
814
|
this.props.raw[prop] = value
|
|
811
815
|
|
|
816
|
+
if (prop === 'color' && typeof value === 'number') {
|
|
817
|
+
// A gradient can share its base color with the new solid color (notably zero).
|
|
818
|
+
// The renderer skips equal base colors, so reset its sides in that case.
|
|
819
|
+
if (this.node.color === value) {
|
|
820
|
+
this.node.colorTop = value
|
|
821
|
+
this.node.colorBottom = value
|
|
822
|
+
this.node.colorLeft = value
|
|
823
|
+
this.node.colorRight = value
|
|
824
|
+
}
|
|
825
|
+
// Use the renderer setter to invalidate colors, including text colors.
|
|
826
|
+
this.node.color = value
|
|
827
|
+
return
|
|
828
|
+
}
|
|
829
|
+
|
|
812
830
|
this.props.props = {}
|
|
813
831
|
// @ts-ignore
|
|
814
832
|
this.props[prop] = unpackTransition(value)
|
|
@@ -908,6 +926,7 @@ const Element = {
|
|
|
908
926
|
if (transition.start !== undefined && typeof transition.start === 'function') {
|
|
909
927
|
// fire transition start callback when animation really starts (depending on specified delay)
|
|
910
928
|
f.once('animating', () => {
|
|
929
|
+
if (this.eol === true) return
|
|
911
930
|
transition.start.call(this.component, this, prop, startValue)
|
|
912
931
|
})
|
|
913
932
|
}
|
|
@@ -929,6 +948,7 @@ const Element = {
|
|
|
929
948
|
}
|
|
930
949
|
|
|
931
950
|
f.once('stopped', () => {
|
|
951
|
+
if (this.eol === true) return
|
|
932
952
|
if (
|
|
933
953
|
this.scheduledTransitions[prop] !== undefined &&
|
|
934
954
|
this.scheduledTransitions[prop].canceled === true
|
|
@@ -936,7 +956,7 @@ const Element = {
|
|
|
936
956
|
return
|
|
937
957
|
}
|
|
938
958
|
// fire transition end callback when animation ends (if specified)
|
|
939
|
-
if (
|
|
959
|
+
if (transition.end && typeof transition.end === 'function') {
|
|
940
960
|
transition.end.call(this.component, this, prop, this.node[prop])
|
|
941
961
|
}
|
|
942
962
|
// remove the prop from scheduled transitions
|
|
@@ -1010,7 +1030,7 @@ const Element = {
|
|
|
1010
1030
|
delete this.forComponent
|
|
1011
1031
|
|
|
1012
1032
|
this.node.destroy()
|
|
1013
|
-
this.node =
|
|
1033
|
+
this.node = undefined
|
|
1014
1034
|
},
|
|
1015
1035
|
get nodeId() {
|
|
1016
1036
|
return this.node && this.node.id
|
package/src/focus/focus.js
CHANGED
|
@@ -29,6 +29,12 @@ let setFocusTimeout
|
|
|
29
29
|
|
|
30
30
|
export const keyUpCallbacks = new Map()
|
|
31
31
|
|
|
32
|
+
export const removeKeyUpCallbacks = (component) => {
|
|
33
|
+
for (const [keyCode, entry] of keyUpCallbacks) {
|
|
34
|
+
if (entry.component === component) keyUpCallbacks.delete(keyCode)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
32
38
|
export default {
|
|
33
39
|
_hold: false,
|
|
34
40
|
set hold(v) {
|
|
@@ -99,7 +105,7 @@ export default {
|
|
|
99
105
|
}
|
|
100
106
|
|
|
101
107
|
if (cb !== undefined) {
|
|
102
|
-
keyUpCallbacks.set(event.keyCode, cb)
|
|
108
|
+
keyUpCallbacks.set(event.keyCode, { callback: cb, component: componentWithInputEvent })
|
|
103
109
|
}
|
|
104
110
|
},
|
|
105
111
|
}
|
package/src/helpers/fetchJson.js
CHANGED
|
@@ -22,10 +22,18 @@ export default (file) => {
|
|
|
22
22
|
if (xhr.readyState == XMLHttpRequest.DONE) {
|
|
23
23
|
// file protocol returns 0
|
|
24
24
|
// http(s) protocol returns 200
|
|
25
|
-
if (xhr.status === 0 || xhr.status === 200)
|
|
26
|
-
|
|
25
|
+
if (xhr.status === 0 || xhr.status === 200) {
|
|
26
|
+
try {
|
|
27
|
+
resolve(JSON.parse(xhr.responseText))
|
|
28
|
+
} catch (err) {
|
|
29
|
+
reject(err)
|
|
30
|
+
}
|
|
31
|
+
} else reject(xhr.statusText || xhr.status)
|
|
27
32
|
}
|
|
28
33
|
}
|
|
34
|
+
|
|
35
|
+
xhr.onerror = () => reject(new Error('Network request failed'))
|
|
36
|
+
|
|
29
37
|
xhr.open('GET', file)
|
|
30
38
|
xhr.send(null)
|
|
31
39
|
})
|
|
@@ -223,6 +223,9 @@ const generateElementCode = function (
|
|
|
223
223
|
|
|
224
224
|
if (key === 'key') return
|
|
225
225
|
|
|
226
|
+
// a ref on a component belongs to the component itself, not to its holder element
|
|
227
|
+
if (options.holder === true && (key === 'ref' || key === ':ref')) return
|
|
228
|
+
|
|
226
229
|
// Skip inspector-data in production builds for performance optimization
|
|
227
230
|
if (key === 'inspector-data' && !isDev) return
|
|
228
231
|
|
|
@@ -524,6 +527,8 @@ const generateForLoopCode = function (templateObject, parent) {
|
|
|
524
527
|
}
|
|
525
528
|
}
|
|
526
529
|
|
|
530
|
+
const invalidateSelectCache = 'ref' in templateObject
|
|
531
|
+
|
|
527
532
|
ctx.renderCode.push(`
|
|
528
533
|
created[${forStartCounter}] = []
|
|
529
534
|
effects[${forStartCounter}] = []
|
|
@@ -532,6 +537,7 @@ const generateForLoopCode = function (templateObject, parent) {
|
|
|
532
537
|
let to${forStartCounter}
|
|
533
538
|
|
|
534
539
|
forloops[${forStartCounter}] = (collection = [], elms, created) => {
|
|
540
|
+
${invalidateSelectCache ? "rootComponent && rootComponent[Symbol.for('invalidateSelectCache')]()" : ''}
|
|
535
541
|
const rawCollection = getRaw(collection)
|
|
536
542
|
const keys = new Set()
|
|
537
543
|
let l = rawCollection.length
|
package/src/lib/colors/colors.js
CHANGED
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
* SPDX-License-Identifier: Apache-2.0
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import
|
|
18
|
+
import htmlColors from './htmlColors.js'
|
|
19
|
+
|
|
20
|
+
const colorsMap = { ...htmlColors }
|
|
19
21
|
|
|
20
22
|
const hex = /^#?([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i
|
|
21
23
|
const rgba =
|
|
@@ -18,14 +18,17 @@
|
|
|
18
18
|
let currentEffect = null
|
|
19
19
|
let currentKey = null
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
// counter that tracks all pauseTracking invocations
|
|
22
|
+
// to ensure nested array operations don't prematurely
|
|
23
|
+
// resume before all operations have completed
|
|
24
|
+
let pauseCounter = 0
|
|
22
25
|
|
|
23
26
|
export const pauseTracking = () => {
|
|
24
|
-
|
|
27
|
+
pauseCounter++
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
export const resumeTracking = () => {
|
|
28
|
-
|
|
31
|
+
if (pauseCounter > 0) pauseCounter--
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
const objectMap = new WeakMap()
|
|
@@ -45,7 +48,7 @@ export const removeEffects = (effectsToRemove) => {
|
|
|
45
48
|
|
|
46
49
|
export const track = (target, key) => {
|
|
47
50
|
if (currentEffect !== null) {
|
|
48
|
-
if (
|
|
51
|
+
if (pauseCounter > 0) {
|
|
49
52
|
return
|
|
50
53
|
}
|
|
51
54
|
// note: nesting the conditions like this seems to perform better ¯\_(ツ)_/¯
|
|
@@ -76,7 +79,7 @@ export const track = (target, key) => {
|
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
export const trigger = (target, key, force = false) => {
|
|
79
|
-
if (
|
|
82
|
+
if (pauseCounter > 0) return
|
|
80
83
|
const effectsMap = objectMap.get(target)
|
|
81
84
|
if (effectsMap === undefined) {
|
|
82
85
|
return
|
|
@@ -89,10 +92,17 @@ export const trigger = (target, key, force = false) => {
|
|
|
89
92
|
}
|
|
90
93
|
}
|
|
91
94
|
|
|
92
|
-
export const effect = (
|
|
93
|
-
|
|
95
|
+
export const effect = (effectFn, key = null) => {
|
|
96
|
+
const previousEffect = currentEffect
|
|
97
|
+
const previousKey = currentKey
|
|
98
|
+
|
|
99
|
+
currentEffect = effectFn
|
|
94
100
|
currentKey = key
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
effectFn()
|
|
104
|
+
} finally {
|
|
105
|
+
currentEffect = previousEffect
|
|
106
|
+
currentKey = previousKey
|
|
107
|
+
}
|
|
98
108
|
}
|
|
@@ -67,9 +67,19 @@ const reactiveProxy = (original, _parent = null, _key) => {
|
|
|
67
67
|
// augment array path methods (that change the length of the array)
|
|
68
68
|
if (arrayPatchMethods.indexOf(key) !== -1) {
|
|
69
69
|
return function (...args) {
|
|
70
|
+
const oldLength = target.length
|
|
70
71
|
pauseTracking()
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
let result
|
|
73
|
+
try {
|
|
74
|
+
result = target[key].apply(this, args)
|
|
75
|
+
} finally {
|
|
76
|
+
resumeTracking()
|
|
77
|
+
}
|
|
78
|
+
// trigger a change when the length of the array has changed
|
|
79
|
+
// by the array operation
|
|
80
|
+
if (_parent === null && target.length !== oldLength) {
|
|
81
|
+
trigger(target, 'length')
|
|
82
|
+
}
|
|
73
83
|
// trigger a change on the parent object and the key
|
|
74
84
|
// i.e. when pushing a new item to `obj.data`, _parent will equal `obj`
|
|
75
85
|
// and _key will equal `data`
|
|
@@ -78,6 +88,10 @@ const reactiveProxy = (original, _parent = null, _key) => {
|
|
|
78
88
|
}
|
|
79
89
|
}
|
|
80
90
|
if (key === 'length') {
|
|
91
|
+
// only track root level to prevent double reactive executions
|
|
92
|
+
if (_parent === null) {
|
|
93
|
+
track(target, 'length')
|
|
94
|
+
}
|
|
81
95
|
return original.length
|
|
82
96
|
}
|
|
83
97
|
|
package/src/lib/symbols.js
CHANGED
|
@@ -144,4 +144,6 @@ export default {
|
|
|
144
144
|
removeEffects: Symbol.for('removeEffects'),
|
|
145
145
|
// Symbol 'tagContent' utilized within generated code
|
|
146
146
|
tagContent: Symbol.for('tagContent'),
|
|
147
|
+
// Symbol used by generated loops to invalidate cached ref lookups
|
|
148
|
+
invalidateSelectCache: Symbol.for('invalidateSelectCache'),
|
|
147
149
|
}
|
package/src/plugins/language.js
CHANGED
|
@@ -55,7 +55,7 @@ export default {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
const setDict = (language) => {
|
|
58
|
-
return new Promise((resolve) => {
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
59
|
const translationObj = translations[language]
|
|
60
60
|
if (typeof translationObj === 'object') {
|
|
61
61
|
dictionary = translationObj
|
|
@@ -71,6 +71,7 @@ export default {
|
|
|
71
71
|
})
|
|
72
72
|
.catch((e) => {
|
|
73
73
|
Log.error(e)
|
|
74
|
+
reject(e)
|
|
74
75
|
})
|
|
75
76
|
}
|
|
76
77
|
})
|