@lightningjs/blits 0.6.13 → 0.7.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 CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ # v0.7.1
4
+
5
+ _22 feb 2024_
6
+
7
+ - Fixed clean up of children nodes when routing to a new view
8
+ - Fixed test cases for code generator (100% coverage)
9
+ - Added support for colorizing sprites
10
+ - Removed before setup and setup lifecycle events and optimized lifecycle instance in components
11
+ - Added support for (re)assigning an array used in a for-loop
12
+ - Refactored delay of transitions (using the now available built-in delay in the renderer)
13
+ - Added preliminary support for frameTick hook
14
+ - Upgraded to latest version of the renderer (0.7.4)
15
+
16
+ # v0.7.0
17
+
18
+ _20 feb 2024_
19
+
20
+
21
+ - Added support for dynamic components through the `is`-attribute (`<Component is="Poster" />` or `<Component is="$dynamicComponent" />`)
22
+ - Added functionality to pass extra data / props when navigating to a new route ( _breaking change_ in signature of `router.to()` method - previously: `router.to(path, options)`, now: `router.to(path, data, options)`)
23
+ - Added configuration option to set the canvas color (aka clear color)
24
+
3
25
  # v0.6.13
4
26
 
5
27
  _16 feb 2024_
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightningjs/blits",
3
- "version": "0.6.13",
3
+ "version": "0.7.1",
4
4
  "description": "Blits: The Lightning 3 App Development Framework",
5
5
  "bin": "bin/index.js",
6
6
  "exports": {
@@ -45,7 +45,7 @@
45
45
  "tape": "^5.5.0"
46
46
  },
47
47
  "dependencies": {
48
- "@lightningjs/renderer": "^0.7.2",
48
+ "@lightningjs/renderer": "^0.7.4",
49
49
  "@lightningjs/vite-plugin-import-chunk-url": "^0.3.0",
50
50
  "execa": "^8.0.1",
51
51
  "kolorist": "^1.8.0",
@@ -124,7 +124,13 @@ declare namespace Component {
124
124
  /**
125
125
  * Fires when the Component is being destroyed and removed.
126
126
  */
127
- destroy?: (this: ComponentInstance) => void
127
+ destroy?: (this: ComponentInstance) => void,
128
+ /**
129
+ * Fires upon each frame start (allowing you to tap directly into the renderloop)
130
+ *
131
+ * Note: This hook will fire continuously, multiple times per second!
132
+ */
133
+ frameTick?: (this: ComponentInstance) => void
128
134
  }
129
135
 
130
136
  export interface ComponentInstance {
@@ -142,9 +148,9 @@ declare namespace Component {
142
148
  * Set a timeout that is automatically cleaned upon component destroy
143
149
  */
144
150
  $setTimeout: (callback: (args: any) => void, ms?: number | undefined) => ReturnType<typeof setTimeout>
145
-
151
+
146
152
  /**
147
- * Clear a timeout
153
+ * Clear a timeout
148
154
  */
149
155
  $clearTimeout: (id: ReturnType<typeof setTimeout>) => void
150
156
 
@@ -152,12 +158,12 @@ declare namespace Component {
152
158
  * Set an interval that is automatically cleaned upon component destroy
153
159
  */
154
160
  $setInterval: (callback: (args: any) => void, ms?: number | undefined) => ReturnType<typeof setInterval>
155
-
161
+
156
162
  /**
157
- * Clear a interval
163
+ * Clear a interval
158
164
  */
159
165
  $clearInterval: (id: ReturnType<typeof setInterval>) => void
160
-
166
+
161
167
  /**
162
168
  * Log to the console with prettier output and configurable debug levels in Settings
163
169
  */
package/src/component.js CHANGED
@@ -19,9 +19,10 @@ import parser from './lib/templateparser/parser.js'
19
19
  import codegenerator from './lib/codegenerator/generator.js'
20
20
 
21
21
  import element from './element.js'
22
+ import { renderer } from './launch.js'
22
23
 
23
24
  import { createHumanReadableId, createInternalId } from './lib/componentId.js'
24
- import { registerHooks, emit, privateEmit } from './lib/hooks.js'
25
+ import { registerHooks, emit } from './lib/hooks.js'
25
26
 
26
27
  import { reactive } from './lib/reactivity/reactive.js'
27
28
  import setupBase from './lib/setup/base.js'
@@ -34,6 +35,7 @@ import setupRoutes from './lib/setup/routes.js'
34
35
  import setupWatch from './lib/setup/watch.js'
35
36
  import { effect } from './lib/reactivity/effect.js'
36
37
  import { Log } from './lib/log.js'
38
+ import lifecycle from './lib/lifecycle.js'
37
39
 
38
40
  import Settings from './settings.js'
39
41
  import symbols from './lib/symbols.js'
@@ -47,18 +49,17 @@ const required = (name) => {
47
49
  }
48
50
 
49
51
  const Component = (name = required('name'), config = required('config')) => {
50
- const setupComponent = (lifecycle, parentComponent) => {
52
+ const setupComponent = (parentComponent) => {
51
53
  // code generation
52
54
  if (!config.code) {
53
55
  Log.debug(`Generating code for ${name} component`)
54
56
  config.code = codegenerator.call(config, parser(config.template, name, parentComponent))
55
57
  }
56
58
 
57
- setupBase(component)
59
+ setupBase(component, name)
58
60
 
59
61
  // setup hooks
60
62
  registerHooks(config.hooks, name)
61
- lifecycle.state = 'beforeSetup'
62
63
 
63
64
  // setup props
64
65
  // if (config.props) // because of the default props like id - might change this
@@ -83,37 +84,20 @@ const Component = (name = required('name'), config = required('config')) => {
83
84
  if (config.input) setupInput(component, config.input)
84
85
 
85
86
  component.setup = true
86
- lifecycle.state = 'setup'
87
87
  }
88
88
 
89
- const createLifecycle = (scope) => {
90
- const states = ['init', 'beforeSetup', 'setup', 'ready', 'focus', 'unfocus', 'destroy']
91
-
92
- return {
89
+ const component = function (opts, parentEl, parentComponent) {
90
+ this.lifecycle = Object.assign(Object.create(lifecycle), {
91
+ component: this,
93
92
  previous: null,
94
93
  current: null,
95
- get state() {
96
- return this.current
97
- },
98
- set state(v) {
99
- if (states.indexOf(v) > -1 && v !== this.current) {
100
- Log.debug(`Setting lifecycle state from ${this.current} to ${v} for ${scope.componentId}`)
101
- this.previous = this.current
102
- this.current = v
103
- // emit 'private' hook
104
- privateEmit(v, name, scope)
105
- // emit 'public' hook
106
- emit(v, name, scope)
107
- }
108
- },
109
- }
110
- }
111
-
112
- const component = function (opts, parentEl, parentComponent) {
113
- this.lifecycle = createLifecycle(this)
94
+ })
114
95
 
115
96
  if (!component.setup) {
116
- setupComponent(this.lifecycle, parentComponent)
97
+ setupComponent(parentComponent)
98
+ }
99
+ if (config.hooks && config.hooks.frameTick) {
100
+ renderer.on('frameTick', (r, data) => emit('frameTick', name, this, [data]))
117
101
  }
118
102
 
119
103
  this.parent = parentComponent
@@ -96,9 +96,11 @@ export default () =>
96
96
  props4['frame'] = 'fps'
97
97
 
98
98
  if (!elms[4]) {
99
+ const componentType = props4['is'] || 'Sprite'
100
+
99
101
  elms[4] = (
100
- (context.components && context.components['Sprite']) ||
101
- component[Symbol.for('components')]['Sprite'] ||
102
+ (context.components && context.components[componentType]) ||
103
+ component[Symbol.for('components')][componentType] ||
102
104
  (() => {
103
105
  console.error('component Sprite not found')
104
106
  })
@@ -162,9 +164,11 @@ export default () =>
162
164
  props7['frame'] = component.fps[0]
163
165
 
164
166
  if (!elms[7]) {
167
+ const componentType = props7['is'] || 'Sprite'
168
+
165
169
  elms[7] = (
166
- (context.components && context.components['Sprite']) ||
167
- component[Symbol.for('components')]['Sprite'] ||
170
+ (context.components && context.components[componentType]) ||
171
+ component[Symbol.for('components')][componentType] ||
168
172
  (() => {
169
173
  console.error('component Sprite not found')
170
174
  })
@@ -214,9 +218,11 @@ export default () =>
214
218
  props9['frame'] = component.fps[1]
215
219
 
216
220
  if (!elms[9]) {
221
+ const componentType = props9['is'] || 'Sprite'
222
+
217
223
  elms[9] = (
218
- (context.components && context.components['Sprite']) ||
219
- component[Symbol.for('components')]['Sprite'] ||
224
+ (context.components && context.components[componentType]) ||
225
+ component[Symbol.for('components')][componentType] ||
220
226
  (() => {
221
227
  console.error('component Sprite not found')
222
228
  })
@@ -266,9 +272,11 @@ export default () =>
266
272
  props11['frame'] = component.fps[2]
267
273
 
268
274
  if (!elms[11]) {
275
+ const componentType = props11['is'] || 'Sprite'
276
+
269
277
  elms[11] = (
270
- (context.components && context.components['Sprite']) ||
271
- component[Symbol.for('components')]['Sprite'] ||
278
+ (context.components && context.components[componentType]) ||
279
+ component[Symbol.for('components')][componentType] ||
272
280
  (() => {
273
281
  console.error('component Sprite not found')
274
282
  })
@@ -332,9 +340,11 @@ export default () =>
332
340
  props14['frame'] = 'avg'
333
341
 
334
342
  if (!elms[14]) {
343
+ const componentType = props14['is'] || 'Sprite'
344
+
335
345
  elms[14] = (
336
- (context.components && context.components['Sprite']) ||
337
- component[Symbol.for('components')]['Sprite'] ||
346
+ (context.components && context.components[componentType]) ||
347
+ component[Symbol.for('components')][componentType] ||
338
348
  (() => {
339
349
  console.error('component Sprite not found')
340
350
  })
@@ -398,9 +408,11 @@ export default () =>
398
408
  props17['frame'] = component.avgFps[0]
399
409
 
400
410
  if (!elms[17]) {
411
+ const componentType = props17['is'] || 'Sprite'
412
+
401
413
  elms[17] = (
402
- (context.components && context.components['Sprite']) ||
403
- component[Symbol.for('components')]['Sprite'] ||
414
+ (context.components && context.components[componentType]) ||
415
+ component[Symbol.for('components')][componentType] ||
404
416
  (() => {
405
417
  console.error('component Sprite not found')
406
418
  })
@@ -450,9 +462,11 @@ export default () =>
450
462
  props19['frame'] = component.avgFps[1]
451
463
 
452
464
  if (!elms[19]) {
465
+ const componentType = props19['is'] || 'Sprite'
466
+
453
467
  elms[19] = (
454
- (context.components && context.components['Sprite']) ||
455
- component[Symbol.for('components')]['Sprite'] ||
468
+ (context.components && context.components[componentType]) ||
469
+ component[Symbol.for('components')][componentType] ||
456
470
  (() => {
457
471
  console.error('component Sprite not found')
458
472
  })
@@ -502,9 +516,11 @@ export default () =>
502
516
  props21['frame'] = component.avgFps[2]
503
517
 
504
518
  if (!elms[21]) {
519
+ const componentType = props21['is'] || 'Sprite'
520
+
505
521
  elms[21] = (
506
- (context.components && context.components['Sprite']) ||
507
- component[Symbol.for('components')]['Sprite'] ||
522
+ (context.components && context.components[componentType]) ||
523
+ component[Symbol.for('components')][componentType] ||
508
524
  (() => {
509
525
  console.error('component Sprite not found')
510
526
  })
@@ -569,9 +585,11 @@ export default () =>
569
585
  props24['frame'] = 'min'
570
586
 
571
587
  if (!elms[24]) {
588
+ const componentType = props24['is'] || 'Sprite'
589
+
572
590
  elms[24] = (
573
- (context.components && context.components['Sprite']) ||
574
- component[Symbol.for('components')]['Sprite'] ||
591
+ (context.components && context.components[componentType]) ||
592
+ component[Symbol.for('components')][componentType] ||
575
593
  (() => {
576
594
  console.error('component Sprite not found')
577
595
  })
@@ -635,9 +653,11 @@ export default () =>
635
653
  props27['frame'] = component.minFps[0]
636
654
 
637
655
  if (!elms[27]) {
656
+ const componentType = props27['is'] || 'Sprite'
657
+
638
658
  elms[27] = (
639
- (context.components && context.components['Sprite']) ||
640
- component[Symbol.for('components')]['Sprite'] ||
659
+ (context.components && context.components[componentType]) ||
660
+ component[Symbol.for('components')][componentType] ||
641
661
  (() => {
642
662
  console.error('component Sprite not found')
643
663
  })
@@ -687,9 +707,11 @@ export default () =>
687
707
  props29['frame'] = component.minFps[1]
688
708
 
689
709
  if (!elms[29]) {
710
+ const componentType = props29['is'] || 'Sprite'
711
+
690
712
  elms[29] = (
691
- (context.components && context.components['Sprite']) ||
692
- component[Symbol.for('components')]['Sprite'] ||
713
+ (context.components && context.components[componentType]) ||
714
+ component[Symbol.for('components')][componentType] ||
693
715
  (() => {
694
716
  console.error('component Sprite not found')
695
717
  })
@@ -739,9 +761,11 @@ export default () =>
739
761
  props31['frame'] = component.minFps[2]
740
762
 
741
763
  if (!elms[31]) {
764
+ const componentType = props31['is'] || 'Sprite'
765
+
742
766
  elms[31] = (
743
- (context.components && context.components['Sprite']) ||
744
- component[Symbol.for('components')]['Sprite'] ||
767
+ (context.components && context.components[componentType]) ||
768
+ component[Symbol.for('components')][componentType] ||
745
769
  (() => {
746
770
  console.error('component Sprite not found')
747
771
  })
@@ -804,9 +828,11 @@ export default () =>
804
828
  props34['frame'] = 'max'
805
829
 
806
830
  if (!elms[34]) {
831
+ const componentType = props34['is'] || 'Sprite'
832
+
807
833
  elms[34] = (
808
- (context.components && context.components['Sprite']) ||
809
- component[Symbol.for('components')]['Sprite'] ||
834
+ (context.components && context.components[componentType]) ||
835
+ component[Symbol.for('components')][componentType] ||
810
836
  (() => {
811
837
  console.error('component Sprite not found')
812
838
  })
@@ -870,9 +896,11 @@ export default () =>
870
896
  props37['frame'] = component.maxFps[0]
871
897
 
872
898
  if (!elms[37]) {
899
+ const componentType = props37['is'] || 'Sprite'
900
+
873
901
  elms[37] = (
874
- (context.components && context.components['Sprite']) ||
875
- component[Symbol.for('components')]['Sprite'] ||
902
+ (context.components && context.components[componentType]) ||
903
+ component[Symbol.for('components')][componentType] ||
876
904
  (() => {
877
905
  console.error('component Sprite not found')
878
906
  })
@@ -922,9 +950,11 @@ export default () =>
922
950
  props39['frame'] = component.maxFps[1]
923
951
 
924
952
  if (!elms[39]) {
953
+ const componentType = props39['is'] || 'Sprite'
954
+
925
955
  elms[39] = (
926
- (context.components && context.components['Sprite']) ||
927
- component[Symbol.for('components')]['Sprite'] ||
956
+ (context.components && context.components[componentType]) ||
957
+ component[Symbol.for('components')][componentType] ||
928
958
  (() => {
929
959
  console.error('component Sprite not found')
930
960
  })
@@ -974,9 +1004,11 @@ export default () =>
974
1004
  props41['frame'] = component.maxFps[2]
975
1005
 
976
1006
  if (!elms[41]) {
1007
+ const componentType = props41['is'] || 'Sprite'
1008
+
977
1009
  elms[41] = (
978
- (context.components && context.components['Sprite']) ||
979
- component[Symbol.for('components')]['Sprite'] ||
1010
+ (context.components && context.components[componentType]) ||
1011
+ component[Symbol.for('components')][componentType] ||
980
1012
  (() => {
981
1013
  console.error('component Sprite not found')
982
1014
  })
@@ -44,10 +44,13 @@ export default () =>
44
44
  function anonymous(component, elms, context) {
45
45
  elms[0].set('texture', component.texture)
46
46
  },
47
+ function anonymous(component, elms, context) {
48
+ elms[0].set('color', component.color)
49
+ },
47
50
  ],
48
51
  context: {},
49
52
  },
50
- props: ['image', 'map', 'frame', 'w', 'h'],
53
+ props: ['image', 'map', 'frame', 'w', 'h', 'color'],
51
54
  state() {
52
55
  return {
53
56
  spriteTexture: false,
@@ -22,9 +22,9 @@ import symbols from '../lib/symbols.js'
22
22
  export default () =>
23
23
  Component('Sprite', {
24
24
  template: `
25
- <Element w="$w" h="$h" :texture="$texture" />
25
+ <Element w="$w" h="$h" :texture="$texture" :color="$color" />
26
26
  `,
27
- props: ['image', 'map', 'frame', 'w', 'h'],
27
+ props: ['image', 'map', 'frame', 'w', 'h', 'color'],
28
28
  state() {
29
29
  return {
30
30
  spriteTexture: false,
package/src/element.js CHANGED
@@ -304,39 +304,44 @@ const Element = {
304
304
  ? transition.function
305
305
  : 'ease'
306
306
  : 'ease',
307
+ delay: typeof transition === 'object' ? ('delay' in transition ? transition.delay : 0) : 0,
307
308
  })
308
309
 
309
310
  // schedule transition
310
311
  return new Promise((resolve) => {
312
+ const startValue = this.node[prop]
311
313
  this.scheduledTransitions[prop] = {
312
314
  v: props[prop],
313
- f,
314
- timeout: setTimeout(() => {
315
- // fire transition start callback if specified
316
- transition.start &&
317
- typeof transition.start === 'function' &&
318
- transition.start.call(this.component, this, prop, props[prop])
319
-
320
- // start the animation
315
+ f: () => {
321
316
  try {
322
317
  f.start()
323
- .waitUntilStopped()
324
- .then(() => delete this.scheduledTransitions[prop])
325
- .then(() => {
326
- // fire transition end callback if specified
327
- transition.end &&
328
- typeof transition.end === 'function' &&
329
- transition.end.call(this.component, this, prop, this.node[prop])
318
+ .waitUntilStarted()
319
+ .then((animation) => {
320
+ // fire transition start callback if specified
321
+ transition.start &&
322
+ typeof transition.start === 'function' &&
323
+ transition.start.call(this.component, this, prop, startValue)
324
+ // continue the chain
325
+ animation
326
+ .waitUntilStopped()
327
+ .then(() => delete this.scheduledTransitions[prop])
328
+ .then(() => {
329
+ // fire transition end callback if specified
330
+ transition.end &&
331
+ typeof transition.end === 'function' &&
332
+ transition.end.call(this.component, this, prop, this.node[prop])
333
+ })
334
+ .then(resolve)
330
335
  })
331
- .then(resolve)
332
336
  } catch (e) {
333
337
  Log.error(e)
334
338
  }
335
- }, transition.delay || 0),
339
+ },
336
340
  }
341
+ this.scheduledTransitions[prop].f()
337
342
  })
338
343
  },
339
- delete() {
344
+ destroy() {
340
345
  Log.debug('Deleting Node', this.nodeId)
341
346
  Object.values(this.scheduledTransitions).forEach((scheduledTransition) => {
342
347
  clearTimeout(scheduledTransition.timeout)
package/src/launch.d.ts CHANGED
@@ -162,6 +162,16 @@ export interface Settings {
162
162
  * `navigator.hardwareConcurrency` (or 2 if `navigator.hardwareConcurrency` is not supported)
163
163
  */
164
164
  webWorkersLimit?: number
165
+ /**
166
+ * Background color of the canvas (also known as the clearColor)
167
+ *
168
+ * Can be a color name (red, blue, silver), a hexadecimal color (`#000000`, `#ccc`),
169
+ * or a color number in rgba order (`0xff0033ff`)
170
+ *
171
+ * Defauls to transparent (`0x00000000`)
172
+ *
173
+ */
174
+ canvasColor?: string,
165
175
  }
166
176
 
167
177
  /**
package/src/launch.js CHANGED
@@ -20,7 +20,7 @@ import { MainCoreDriver, RendererMain } from '@lightningjs/renderer'
20
20
  import Settings from './settings.js'
21
21
  import { initLog, Log } from './lib/log.js'
22
22
  import { screenResolutions } from './lib/utils.js'
23
- // import coreExtensionModule from './fontLoader.js?importChunkUrl'
23
+ import colors from './lib/colors/colors.js'
24
24
 
25
25
  export let renderer
26
26
 
@@ -51,6 +51,7 @@ export default (App, target, settings) => {
51
51
  'webWorkersLimit' in settings
52
52
  ? settings.webWorkersLimit
53
53
  : window.navigator.hardwareConcurrency || 2,
54
+ clearColor: (settings.canvasColor && colors.normalize(settings.canvasColor)) || 0x00000000,
54
55
  },
55
56
  target,
56
57
  driver
@@ -169,11 +169,11 @@ const generateComponentCode = function (
169
169
 
170
170
  renderCode.push(`
171
171
  if(!${elm}) {
172
- ${elm} = (context.components && context.components['${
172
+ const componentType = props${counter}['is'] || '${
173
173
  templateObject[Symbol.for('componentType')]
174
- }'] || component[Symbol.for('components')]['${
175
- templateObject[Symbol.for('componentType')]
176
- }'] || (() => { console.error('component ${
174
+ }'
175
+
176
+ ${elm} = (context.components && context.components[componentType] || component[Symbol.for('components')][componentType] || (() => { console.error('component ${
177
177
  templateObject[Symbol.for('componentType')]
178
178
  } not found')})).call(null, {props: props${counter}}, ${parent}, component)
179
179
  if (${elm}[Symbol.for('slots')][0]) {