@lightningjs/blits 1.4.2 → 1.5.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 CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## v1.5.0
4
+
5
+ _1 oct 2024_
6
+
7
+ - Fixed issue with interupting running / scheduled transitions with a new transition on the same prop
8
+ - Fixed issue with (incompatible) SDF text renderer being loaded when `renderMode` is set to convas
9
+ - Added support for handling key release (i.e `keyup`) events
10
+
11
+ ## v1.4.3
12
+
13
+ _26 sept 2024_
14
+
15
+ - Fixed issues with sprites in minified production build
16
+ - Bumped renderer to v2.2.0 (with fixes for nested alpha / show issue)
17
+ - Fixed reactivity in plugins not using reactivity mode setting
18
+ - Fixed named slots functionality
19
+
3
20
  ## v1.4.2
4
21
 
5
22
  _24 sept 2024_
@@ -1,7 +1,6 @@
1
1
  # Handling User Input
2
2
 
3
- In order to allow users to interact with your app, you will want to capture and handle _user input_. If you are developing a TV based app this will
4
- often be key input via a remote control.
3
+ In order to allow users to interact with your app, you will want to capture and handle _user input_. If you are developing a TV based app this will often be key input via a remote control.
5
4
 
6
5
  Blits offers an intuitive and straightforward interface to handle key input in Components.
7
6
 
@@ -73,6 +72,53 @@ When a component handles a key press by having a corresponding function specifie
73
72
  }
74
73
  ```
75
74
 
75
+ ## Key-up handling
76
+
77
+ The functions specified in the `input` configuration are invoked when a key is _pressed down_ (i.e. the `keydown` event listener). But sometimes you may also want to execute some logic when a key is _released_ (i.e. the `keyup` event listener).
78
+
79
+ Instead of introducing a separate key on the Component configuration object for key release callbacks, Blits relies on the concept that a `keyup` event is always preceeded by a `keydown` event.
80
+
81
+ Following this logic, whenever you return a function in an input (key down) handler, this function will be executed upon release (i.e. the `keyup` event) of that key .
82
+
83
+ When an input key is being a hold down, it will execute the key down handler multipe times. Upon key release, only the last returned key up callback function will be executed.
84
+
85
+
86
+ ```javascript
87
+ Blits.Component('MyComponemnt', {
88
+ //
89
+ input: {
90
+ enter() {
91
+ // execute logic on key down
92
+ this.pressedEnter = true
93
+ return () => {
94
+ // execute logic on key up
95
+ this.pressedEnter = false
96
+ }
97
+ },
98
+ space(e) {
99
+ // not logic on key down
100
+ return () => {
101
+ // only execute logic on key up
102
+ console.log('Space key released')
103
+ }
104
+ },
105
+ left() {
106
+ // some logic on key down here ..
107
+ this.leftHold = true
108
+ // return a reference to a Component method
109
+ // (instead of creating a new function on the fly)
110
+ return this.leftKeyUp
111
+ }
112
+ },
113
+ methods: {
114
+ leftKeyUp() {
115
+ console.log('Left key up')
116
+ this.leftHold = false
117
+ }
118
+ }
119
+ }
120
+ ```
121
+
76
122
  ## Custom Keycode mapping
77
123
 
78
124
  Blits comes with a default keycode mapping. This mapping is a sensible default that works in your desktop browser and with most RDK based devices.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightningjs/blits",
3
- "version": "1.4.2",
3
+ "version": "1.5.0",
4
4
  "description": "Blits: The Lightning 3 App Development Framework",
5
5
  "bin": "bin/index.js",
6
6
  "exports": {
@@ -47,7 +47,7 @@
47
47
  },
48
48
  "dependencies": {
49
49
  "@lightningjs/msdf-generator": "^1.0.2",
50
- "@lightningjs/renderer": "^2.1.2",
50
+ "@lightningjs/renderer": "^2.3.0",
51
51
  "execa": "^8.0.1",
52
52
  "kolorist": "^1.8.0",
53
53
  "ora": "^7.0.1",
@@ -16,7 +16,7 @@
16
16
  */
17
17
 
18
18
  import Component from './component.js'
19
- import Focus from './focus.js'
19
+ 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'
@@ -65,7 +65,12 @@ const Application = (config) => {
65
65
  }, Settings.get('holdTimeout', DEFAULT_HOLD_TIMEOUT_MS))
66
66
  }
67
67
 
68
- keyUpHandler = () => {
68
+ keyUpHandler = (e) => {
69
+ const cb = keyUpCallbacks.get(e.code)
70
+ if (cb !== undefined && typeof cb === 'function') {
71
+ keyUpCallbacks.delete(e.code)
72
+ cb()
73
+ }
69
74
  clearTimeout(holdTimeout)
70
75
  Focus.hold = false
71
76
  }
@@ -268,7 +268,7 @@ const Element = {
268
268
 
269
269
  this.props.element = this
270
270
 
271
- this.props['parent'] = this.config.parent
271
+ this.props['parent'] = props['parent'] || this.config.parent
272
272
  delete props.parent
273
273
 
274
274
  this.props.raw = data
@@ -305,7 +305,7 @@ const Element = {
305
305
  })
306
306
  }
307
307
 
308
- // if (this.component && this.component.___layout) {
308
+ // if (this.component !== undefined && '___layout' in this.component) {
309
309
  // this.node.on('loaded', () => {
310
310
  // this.component.___layout()
311
311
  // })
@@ -344,16 +344,17 @@ const Element = {
344
344
  // }
345
345
  },
346
346
  animate(prop, value, transition) {
347
+ // check if a transition is already scheduled to run on the same prop
348
+ // and cancels it if it does
349
+ if (
350
+ this.scheduledTransitions[prop] !== undefined &&
351
+ this.scheduledTransitions[prop].f.state === 'scheduled'
352
+ ) {
353
+ this.scheduledTransitions[prop].f.stop()
354
+ }
355
+
347
356
  // if current value is the same as the value to animate to, instantly resolve
348
357
  if (this.node[prop] === value) return
349
- // check if a transition is already scheduled or running on the same prop
350
- if (this.scheduledTransitions[prop]) {
351
- if (this.scheduledTransitions[prop].f.state === 'running') {
352
- // fastforward to final value
353
- this.node[prop] = this.scheduledTransitions[prop].v
354
- // this.scheduledTransitions[prop].f.stop()
355
- }
356
- }
357
358
 
358
359
  const props = {}
359
360
  props[prop] = value
@@ -382,7 +383,6 @@ const Element = {
382
383
  // removed if another transition for the same prop starts in the mean time
383
384
  this.scheduledTransitions[prop] = {
384
385
  v: props[prop],
385
- cancel: false,
386
386
  f,
387
387
  }
388
388
 
@@ -395,7 +395,7 @@ const Element = {
395
395
 
396
396
  f.once('stopped', () => {
397
397
  // remove the prop from scheduled transitions
398
- this.scheduledTransitions[prop] = null
398
+ this.scheduledTransitions[prop] = undefined
399
399
  // fire transition end callback when animation ends (if specified)
400
400
  if (transition.end && typeof transition.end === 'function') {
401
401
  transition.end.call(this.component, this, prop, this.node[prop])
@@ -35,6 +35,13 @@ const renderEngine = (settings) => {
35
35
  if (renderMode === 'canvas') return CanvasCoreRenderer
36
36
  }
37
37
 
38
+ const textRenderEngines = (settings) => {
39
+ const renderMode = 'renderMode' in settings ? settings.renderMode : 'webgl'
40
+
41
+ if (renderMode === 'webgl') return [SdfTextRenderer, CanvasTextRenderer]
42
+ if (renderMode === 'canvas') return [CanvasTextRenderer]
43
+ }
44
+
38
45
  export default (App, target, settings = {}) => {
39
46
  renderer = new RendererMain(
40
47
  {
@@ -57,7 +64,7 @@ export default (App, target, settings = {}) => {
57
64
  txMemByteThreshold:
58
65
  'gpuMemoryLimit' in settings ? settings.gpuMemoryLimit * 1024 * 1024 : 200 * 1024 * 1024,
59
66
  renderEngine: renderEngine(settings),
60
- fontEngines: [SdfTextRenderer, CanvasTextRenderer],
67
+ fontEngines: textRenderEngines(settings),
61
68
  },
62
69
  target
63
70
  )
package/src/focus.js CHANGED
@@ -24,6 +24,8 @@ let focusedComponent = null
24
24
  let focusChain = []
25
25
  let setFocusTimeout
26
26
 
27
+ export const keyUpCallbacks = new Map()
28
+
27
29
  export default {
28
30
  _hold: false,
29
31
  set hold(v) {
@@ -60,10 +62,14 @@ export default {
60
62
  const componentWithInputEvent = focusChain.shift()
61
63
 
62
64
  if (componentWithInputEvent) {
65
+ let cb
63
66
  if (componentWithInputEvent[symbols.inputEvents][key]) {
64
- componentWithInputEvent[symbols.inputEvents][key].call(componentWithInputEvent, event)
67
+ cb = componentWithInputEvent[symbols.inputEvents][key].call(componentWithInputEvent, event)
65
68
  } else if (componentWithInputEvent[symbols.inputEvents].any) {
66
- componentWithInputEvent[symbols.inputEvents].any.call(componentWithInputEvent, event)
69
+ cb = componentWithInputEvent[symbols.inputEvents].any.call(componentWithInputEvent, event)
70
+ }
71
+ if (cb !== undefined) {
72
+ keyUpCallbacks.set(event.code, cb)
67
73
  }
68
74
  }
69
75
  },
@@ -91,7 +91,7 @@ const generateElementCode = function (
91
91
  Object.keys(templateObject).forEach((key) => {
92
92
  if (key === 'slot') {
93
93
  renderCode.push(`
94
- elementConfig${counter}['parent'] = component[Symbol.for('slots')].filter(slot => slot.ref === '${templateObject.slot}').shift() || component[Symbol.for('slots')][0] || parent
94
+ elementConfig${counter}['parent'] = component[Symbol.for('slots')].filter(slot => slot.ref === '${templateObject.slot}').shift() || parent
95
95
  `)
96
96
  }
97
97
 
@@ -258,6 +258,7 @@ const generateComponentCode = function (
258
258
  }
259
259
  // if (!options.forloop) {
260
260
  // renderCode.push(`
261
+ // // console.log('here', component, rootComponent)
261
262
  // component = rootComponent
262
263
  // `)
263
264
  // }
@@ -15,6 +15,7 @@
15
15
  * SPDX-License-Identifier: Apache-2.0
16
16
  */
17
17
 
18
+ import { ImageTexture } from '@lightningjs/renderer'
18
19
  import { track, trigger, pauseTracking, resumeTracking } from './effect.js'
19
20
  import symbols from '../symbols.js'
20
21
 
@@ -32,7 +33,7 @@ const reactiveProxy = (original, _parent = null, _key) => {
32
33
  // is assigned to a state variable
33
34
  if (typeof original === 'object') {
34
35
  if (original[symbols.id] !== undefined) return original
35
- if (original.constructor.name === '_ImageTexture') return original
36
+ if (original.constructor.name === ImageTexture.name) return original
36
37
  }
37
38
 
38
39
  // if original object is already a proxy, don't create a new one but return the existing one instead
@@ -16,10 +16,11 @@
16
16
  */
17
17
 
18
18
  import { reactive } from '../lib/reactivity/reactive'
19
+ import Settings from '../settings.js'
19
20
 
20
21
  export default {
21
22
  name: 'appState',
22
23
  plugin(state = {}) {
23
- return reactive(state)
24
+ return reactive(state, Settings.get('reactivityMode'))
24
25
  },
25
26
  }
@@ -19,16 +19,21 @@ import fetchJson from '../helpers/fetchJson'
19
19
  import { Log } from '../lib/log'
20
20
  import { reactive } from '../lib/reactivity/reactive'
21
21
 
22
+ import Settings from '../settings.js'
23
+
22
24
  export default {
23
25
  name: 'language',
24
26
  plugin(options = {}) {
25
27
  let translations = {}
26
28
  let dictionary = {}
27
29
 
28
- const state = reactive({
29
- language: '',
30
- loaded: 0,
31
- })
30
+ const state = reactive(
31
+ {
32
+ language: '',
33
+ loaded: 0,
34
+ },
35
+ Settings.get('reactivityMode')
36
+ )
32
37
 
33
38
  const setLanguage = (language) => {
34
39
  // define the dictionary
@@ -18,6 +18,8 @@
18
18
  import { Log } from '../lib/log'
19
19
  import { reactive } from '../lib/reactivity/reactive.js'
20
20
 
21
+ import Settings from '../settings.js'
22
+
21
23
  const getValueByDotNotation = (obj, base, path) => {
22
24
  const keys = path.split('.')
23
25
 
@@ -46,9 +48,12 @@ export default {
46
48
  plugin(config = {}) {
47
49
  let themeMap = {}
48
50
 
49
- const state = reactive({
50
- current: 'default',
51
- })
51
+ const state = reactive(
52
+ {
53
+ current: 'default',
54
+ },
55
+ Settings.get('reactivityMode')
56
+ )
52
57
 
53
58
  let themes = {}
54
59
  let base = null