@lightningjs/blits 0.10.0 → 1.0.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/docs/essentials/displaying_text.md +1 -1
  3. package/package.json +3 -3
  4. package/src/component/base/events.js +1 -1
  5. package/src/component/base/utils.js +34 -1
  6. package/src/component/setup/props.js +3 -1
  7. package/src/component/setup/props.test.js +51 -19
  8. package/src/component.d.ts +2 -1
  9. package/src/component.js +20 -13
  10. package/src/components/Circle.js +13 -12
  11. package/src/components/FPScounter.js +388 -536
  12. package/src/components/Image.js +7 -9
  13. package/src/components/Layout.js +49 -0
  14. package/src/components/Layout.orig.js +34 -0
  15. package/src/components/RouterView.js +6 -10
  16. package/src/components/Sprite.js +10 -12
  17. package/src/components/index.js +2 -2
  18. package/src/components/index.orig.js +2 -2
  19. package/src/engine.js +1 -1
  20. package/src/engines/L3/element.js +193 -180
  21. package/src/engines/L3/element.test.js +170 -0
  22. package/src/engines/L3/fontLoader.js +4 -5
  23. package/src/engines/L3/index.js +2 -2
  24. package/src/engines/L3/launch.js +7 -13
  25. package/src/engines/L3/shaderLoader.js +12 -0
  26. package/src/engines/index.js +1 -1
  27. package/src/launch.d.ts +19 -2
  28. package/src/lib/codegenerator/generator.js +212 -84
  29. package/src/lib/codegenerator/generator.test.js +657 -942
  30. package/src/lib/colors/colors.js +59 -46
  31. package/src/lib/precompiler/precompiler.js +2 -1
  32. package/src/lib/reactivity/effect.js +10 -13
  33. package/src/lib/reactivity/reactive.js +24 -14
  34. package/src/lib/symbols.js +1 -0
  35. package/src/router/router.js +17 -3
  36. package/boilerplate/js/default/src/renderer.js +0 -4
  37. package/src/components/Text.js +0 -119
  38. package/src/components/Text.orig.js +0 -81
@@ -15,64 +15,77 @@
15
15
  * SPDX-License-Identifier: Apache-2.0
16
16
  */
17
17
 
18
- import htmlColors from './htmlColors.js'
19
- import { Log } from '../log.js'
18
+ import colorsMap from './htmlColors.js'
19
+
20
+ const hex = /^#?([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i
21
+ const rgba =
22
+ /^(rgba?)\((\s*(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\s*),(\s*(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\s*),(\s*(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\s*)(,(\s*(-?\d+(?:\.\d+)?)\s*))?\)$/
23
+ const hsla =
24
+ /^(hsla?)\((\s*(360|3[0-5][0-9]|[12]?[0-9]{1,2})\s*),(\s*(100|[1-9]?[0-9])\s*)%,(\s*(100|[1-9]?[0-9])\s*)%(,(\s*(1|0(?:\.\d+)?)\s*))?\)$/
25
+
26
+ const hexLookup = {}
27
+
28
+ const decimalToHex = (decimal) => {
29
+ if (hexLookup[decimal] !== undefined) {
30
+ return hexLookup[decimal]
31
+ }
32
+
33
+ const resp = parseInt(decimal).toString(16).padStart(2, '0')
34
+ hexLookup[decimal] = resp
35
+ return resp
36
+ }
20
37
 
21
38
  export default {
22
- // The defaultColor parameter should be set to null when utilizing this function within the template parser.
23
- // Because the template parser evaluates attribute values, even if they are not color codes,
24
- // to determine whether they represent color values or not.
25
- normalize(color = '', defaultColor = '0xffffffff') {
39
+ normalize: (color = '', defaultColor = '0xffffffff') => {
26
40
  color = color.toString()
27
41
 
28
- if (color.length > 0) {
29
- const normalized = /^0x[0-9a-f]{8}$/
30
- const hex = /^#?([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i
31
- const rgba =
32
- /^(rgba?)\((\s*(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\s*),(\s*(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\s*),(\s*(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\s*)(,(\s*(-?\d+(?:\.\d+)?)\s*))?\)$/
33
- const hsla =
34
- /^(hsla?)\((\s*(360|3[0-5][0-9]|[12]?[0-9]{1,2})\s*),(\s*(100|[1-9]?[0-9])\s*)%,(\s*(100|[1-9]?[0-9])\s*)%(,(\s*(1|0(?:\.\d+)?)\s*))?\)$/
42
+ // its mappable, map it
43
+ if (colorsMap[color] !== undefined) {
44
+ return colorsMap[color]
45
+ }
35
46
 
36
- if (normalized.test(color)) {
37
- return color
47
+ // its a 0xRRGGBBAA color, store it and return it
48
+ if (color.startsWith('0x') && color.length === 10) {
49
+ colorsMap[color] = color
50
+ return color
51
+ }
52
+
53
+ // check for hex color
54
+ if (hex.test(color)) {
55
+ color = color.replace('#', '').toLowerCase()
56
+ if (color.length === 3) {
57
+ color = color
58
+ .split('')
59
+ .map((c) => c + c)
60
+ .join('')
38
61
  }
39
62
 
40
- if (hex.test(color)) {
41
- color = color.replace('#', '').toLowerCase()
42
- if (color.length === 3) {
43
- color = color
44
- .split('')
45
- .map((c) => c + c)
46
- .join('')
47
- }
63
+ const colorRGBA = '0x' + color.padEnd(8, 'f')
64
+ colorsMap[color] = colorRGBA
65
+ return colorRGBA
66
+ }
48
67
 
49
- return '0x' + color.padEnd(8, 'f')
50
- }
68
+ //rgb/a
69
+ const rgbaMatch = rgba.exec(color)
70
+ if (rgbaMatch) {
71
+ const r = decimalToHex(rgbaMatch[3])
72
+ const g = decimalToHex(rgbaMatch[5])
73
+ const b = decimalToHex(rgbaMatch[7])
74
+ let a = 'ff'
51
75
 
52
- //rgb/a
53
- const rgbaMatch = rgba.exec(color)
54
- if (rgbaMatch) {
55
- return (
56
- '0x' +
57
- parseInt(rgbaMatch[3]).toString(16).padStart(2, '0') +
58
- parseInt(rgbaMatch[5]).toString(16).padStart(2, '0') +
59
- parseInt(rgbaMatch[7]).toString(16).padStart(2, '0') +
60
- (rgbaMatch[10] && rgbaMatch[1] === 'rgba'
61
- ? Math.min(Math.max(Math.round(parseFloat(rgbaMatch[10]) * 255), 0), 255)
62
- .toString(16)
63
- .padStart(2, '0')
64
- : 'ff')
65
- )
76
+ if (rgbaMatch[10] && rgbaMatch[1] === 'rgba') {
77
+ const alpha = Math.min(Math.max(Math.round(parseFloat(rgbaMatch[10]) * 255), 0), 255)
78
+ a = decimalToHex(alpha)
66
79
  }
67
80
 
68
- if (hsla.test(color)) {
69
- Log.warn('HSL(A) color format is not supported yet')
70
- return '0xffffffff'
71
- }
81
+ const resp = '0x' + r + g + b + a
82
+ colorsMap[color] = resp
83
+ return resp
84
+ }
72
85
 
73
- if (color in htmlColors) {
74
- return htmlColors[color]
75
- }
86
+ if (hsla.test(color)) {
87
+ console.warn('HSL(A) color format is not supported yet')
88
+ return '0xffffffff'
76
89
  }
77
90
 
78
91
  return defaultColor
@@ -23,7 +23,8 @@ export default (source, filePath) => {
23
23
  if (
24
24
  source.indexOf('Blits.Component(') > -1 ||
25
25
  source.indexOf('Blits.Application(') > -1 ||
26
- /=>\s*Component\(['"][A-Za-z]+['"],/s.test(source) // blits component
26
+ /=>\s*Component\(['"][A-Za-z]+['"],/s.test(source) || // blits component
27
+ /\{.*?template\s*:\s*(['"`])((?:\\?.)*?)\1.*?\}/s.test(source) // object with template key
27
28
  ) {
28
29
  const templates = source.matchAll(/(?<!\/\/\s*)template\s*:\s*(['"`])((?:\\?.)*?)\1/gs)
29
30
  let newSource = source
@@ -16,6 +16,7 @@
16
16
  */
17
17
 
18
18
  let currentEffect = null
19
+ let currentKey = null
19
20
 
20
21
  let paused = false
21
22
 
@@ -25,22 +26,16 @@ export const pauseTracking = () => {
25
26
 
26
27
  export const resumeTracking = () => {
27
28
  paused = false
28
- for (let i = 0; i < trackQueue.length; i++) {
29
- const item = trackQueue[i]
30
- currentEffect = item.currentEffect
31
- track(item.target, item.key)
32
- }
33
- trackQueue.length = 0
34
29
  }
35
30
 
36
31
  const objectMap = new WeakMap()
37
32
 
38
- const trackQueue = []
39
-
40
33
  export const track = (target, key) => {
41
34
  if (currentEffect) {
42
35
  if (paused) {
43
- trackQueue.push({ target, key, currentEffect })
36
+ return
37
+ }
38
+ if (currentKey !== null && key !== currentKey) {
44
39
  return
45
40
  }
46
41
  let effectsMap = objectMap.get(target)
@@ -58,21 +53,23 @@ export const track = (target, key) => {
58
53
  }
59
54
 
60
55
  export const trigger = (target, key, force = false) => {
61
- if (paused) return
56
+ if (paused === true) return
62
57
  const effectsMap = objectMap.get(target)
63
58
  if (!effectsMap) {
64
59
  return
65
60
  }
66
61
  const effects = effectsMap.get(key)
67
62
  if (effects) {
68
- effects.forEach((effect) => {
63
+ for (let effect of effects) {
69
64
  effect(force)
70
- })
65
+ }
71
66
  }
72
67
  }
73
68
 
74
- export const effect = (effect) => {
69
+ export const effect = (effect, key = null) => {
75
70
  currentEffect = effect
71
+ currentKey = key
76
72
  currentEffect()
77
73
  currentEffect = null
74
+ currentKey = null
78
75
  }
@@ -18,27 +18,27 @@
18
18
  import { track, trigger, pauseTracking, resumeTracking } from './effect.js'
19
19
  import symbols from '../symbols.js'
20
20
 
21
- const arrayPatchMethods = ['push', 'pop', 'shift', 'unshift', 'splice']
21
+ const arrayPatchMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort']
22
22
 
23
23
  const proxyMap = new WeakMap()
24
24
 
25
- const getProxy = (obj) => {
26
- return proxyMap.get(obj)
27
- }
28
-
29
- const getRaw = (value) => {
25
+ export const getRaw = (value) => {
30
26
  const raw = value && value[symbols.raw]
31
27
  return raw ? getRaw(raw) : value
32
28
  }
33
29
 
34
30
  const reactiveProxy = (original, _parent = null, _key) => {
35
- // don't create a proxy when a Blits component is assigned to a state variable
36
- if (typeof original === 'object' && original[symbols.id]) {
31
+ // don't create a proxy when a Blits component or an Image Texture
32
+ // is assigned to a state variable
33
+ if (
34
+ (typeof original === 'object' && original[symbols.id]) ||
35
+ original.constructor.name === '_ImageTexture'
36
+ ) {
37
37
  return original
38
38
  }
39
39
 
40
40
  // if original object is already a proxy, don't create a new one but return the existing one instead
41
- const existingProxy = getProxy(original)
41
+ const existingProxy = proxyMap.get(original)
42
42
  if (existingProxy) {
43
43
  return existingProxy
44
44
  }
@@ -52,21 +52,31 @@ const reactiveProxy = (original, _parent = null, _key) => {
52
52
 
53
53
  // handling arrays
54
54
  if (Array.isArray(target)) {
55
+ if (typeof target[key] === 'object' && target[key] !== null) {
56
+ if (Array.isArray(target[key])) {
57
+ track(target, key)
58
+ }
59
+ // create a new reactive proxy
60
+ return reactiveProxy(getRaw(target[key]), target, key)
61
+ }
55
62
  // augment array path methods (that change the length of the array)
56
- if (arrayPatchMethods.indexOf(key) > -1) {
63
+ if (arrayPatchMethods.indexOf(key) !== -1) {
57
64
  return function (...args) {
58
65
  pauseTracking()
59
66
  const result = target[key].apply(this, args)
67
+ resumeTracking()
60
68
  // trigger a change on the parent object and the key
61
69
  // i.e. when pushing a new item to `obj.data`, _parent will equal `obj`
62
70
  // and _key will equal `data`
63
- resumeTracking()
64
71
  trigger(_parent, _key)
65
72
  return result
66
73
  }
67
- } else {
68
- return Reflect.get(target, key, receiver)
69
74
  }
75
+ if (key === 'length') {
76
+ return original.length
77
+ }
78
+
79
+ return Reflect.get(target, key, receiver)
70
80
  }
71
81
 
72
82
  // handling objects (but not null values, which have object type in JS)
@@ -75,7 +85,7 @@ const reactiveProxy = (original, _parent = null, _key) => {
75
85
  track(target, key)
76
86
  }
77
87
  // create a new reactive proxy
78
- return reactiveProxy(target[key], target, key)
88
+ return reactiveProxy(getRaw(target[key]), target, key)
79
89
  }
80
90
 
81
91
  // handling all other types
@@ -3,6 +3,7 @@ export default {
3
3
  cursorTagStart: Symbol('cursorTagStart'),
4
4
  computedKeys: Symbol('computedKeys'),
5
5
  destroy: Symbol('destroy'),
6
+ getChildren: Symbol('getChildren'),
6
7
  holder: Symbol('holder'),
7
8
  id: Symbol('id'),
8
9
  identifier: Symbol('identifier'),
@@ -259,9 +259,23 @@ const removeView = async (route, view, transition) => {
259
259
  }
260
260
 
261
261
  const setOrAnimate = (node, transition, shouldAnimate = true) => {
262
- return shouldAnimate
263
- ? node.animate(transition.prop, transition.value, transition)
264
- : node.set(transition.prop, transition.value)
262
+ return new Promise((resolve) => {
263
+ if (shouldAnimate) {
264
+ // resolve the promise in the transition end-callback
265
+ // ("extending" end callback when one is already specified)
266
+ const existingEndCallback = transition.end
267
+ transition.end = existingEndCallback
268
+ ? (...args) => {
269
+ existingEndCallback(...args)
270
+ resolve()
271
+ }
272
+ : resolve
273
+ node.set(transition.prop, { transition })
274
+ } else {
275
+ node.set(transition.prop, transition.value)
276
+ resolve()
277
+ }
278
+ })
265
279
  }
266
280
 
267
281
  export const to = (location, data = {}, options = {}) => {
@@ -1,4 +0,0 @@
1
- // @ts-ignore
2
- import renderer from '@lightningjs/blits/renderer'
3
-
4
- renderer()
@@ -1,119 +0,0 @@
1
- /*
2
- * Copyright 2023 Comcast Cable Communications Management, LLC
3
- * Licensed under the Apache License, Version 2.0 (the "License");
4
- * you may not use this file except in compliance with the License.
5
- * You may obtain a copy of the License at
6
- *
7
- * http://www.apache.org/licenses/LICENSE-2.0
8
- *
9
- * Unless required by applicable law or agreed to in writing, software
10
- * distributed under the License is distributed on an "AS IS" BASIS,
11
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- * See the License for the specific language governing permissions and
13
- * limitations under the License.
14
- *
15
- * SPDX-License-Identifier: Apache-2.0
16
- */
17
-
18
- import Component from '../component.js'
19
- import Settings from '../settings.js'
20
-
21
- export default () =>
22
- Component('Text', {
23
- /* eslint-disable no-unused-vars */
24
- code: {
25
- render: function anonymous(parent, component, context, components) {
26
- const elms = []
27
-
28
- if (!elms[0]) {
29
- elms[0] = this.element({ parent: parent || 'root' }, component)
30
- }
31
- const elementConfig0 = {}
32
-
33
- elementConfig0['__textnode'] = true
34
- elementConfig0['fontFamily'] = component.font
35
- elementConfig0['letterSpacing'] = component.letterspacing
36
- elementConfig0['lineHeight'] = component._lineheight
37
- elementConfig0['stretch'] = component.stretch
38
- elementConfig0['contain'] = component._contain
39
- elementConfig0['@loaded'] = component['@loaded'] && component['@loaded'].bind(component)
40
- elementConfig0['@error'] = component['@error'] && component['@error'].bind(component)
41
-
42
- if (!elms[0].nodeId) {
43
- elms[0].populate(elementConfig0)
44
- }
45
-
46
- return elms
47
- },
48
- effects: [
49
- function anonymous(component, elms, context, components) {
50
- elms[0].set('text', component.content)
51
- },
52
- function anonymous(component, elms, context, components) {
53
- elms[0].set('fontSize', component.size)
54
- },
55
- function anonymous(component, elms, context, components) {
56
- elms[0].set('color', component.color)
57
- },
58
- function anonymous(component, elms, context, components) {
59
- elms[0].set('style', component.style)
60
- },
61
- function anonymous(component, elms, context, components) {
62
- elms[0].set('weight', component.weight)
63
- },
64
- function anonymous(component, elms, context, components) {
65
- elms[0].set('wordWrap', component.wordwrap)
66
- },
67
- function anonymous(component, elms, context, components) {
68
- elms[0].set('maxLines', component.maxlines)
69
- },
70
- function anonymous(component, elms, context, components) {
71
- elms[0].set('h', component.maxheight)
72
- },
73
- function anonymous(component, elms, context, components) {
74
- elms[0].set('textAlign', component.align)
75
- },
76
- function anonymous(component, elms, context, components) {
77
- elms[0].set('overflowSuffix', component.textoverflow)
78
- },
79
- ],
80
- context: {},
81
- },
82
- props: [
83
- 'content',
84
- {
85
- key: 'font',
86
- default: Settings.get('defaultFont', 'lato'),
87
- },
88
- {
89
- key: 'size',
90
- cast: Number,
91
- default: 32,
92
- },
93
- 'style',
94
- 'color',
95
- 'weight',
96
- 'letterspacing',
97
- 'stretch',
98
- 'align',
99
- 'wordwrap',
100
- 'maxlines',
101
- 'maxheight',
102
- 'lineheight',
103
- 'contain',
104
- '@loaded',
105
- '@error',
106
- 'textoverflow',
107
- ],
108
- computed: {
109
- _contain() {
110
- return (
111
- this.contain ||
112
- (this.maxheight && this.wordwrap ? 'both' : this.wordwrap ? 'width' : 'none')
113
- )
114
- },
115
- _lineheight() {
116
- return this.lineheight !== undefined ? this.lineheight : this.size
117
- },
118
- },
119
- })
@@ -1,81 +0,0 @@
1
- /*
2
- * Copyright 2023 Comcast Cable Communications Management, LLC
3
- * Licensed under the Apache License, Version 2.0 (the "License");
4
- * you may not use this file except in compliance with the License.
5
- * You may obtain a copy of the License at
6
- *
7
- * http://www.apache.org/licenses/LICENSE-2.0
8
- *
9
- * Unless required by applicable law or agreed to in writing, software
10
- * distributed under the License is distributed on an "AS IS" BASIS,
11
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- * See the License for the specific language governing permissions and
13
- * limitations under the License.
14
- *
15
- * SPDX-License-Identifier: Apache-2.0
16
- */
17
-
18
- import Component from '../component.js'
19
- import Settings from '../settings.js'
20
-
21
- export default () =>
22
- Component('Text', {
23
- template: `
24
- <Element
25
- __textnode="true"
26
- :text="$content"
27
- fontFamily="$font"
28
- :fontSize="$size"
29
- :color="$color"
30
- :style="$style"
31
- :weight="$weight"
32
- letterSpacing="$letterspacing"
33
- lineHeight="$_lineheight"
34
- stretch="$stretch"
35
- contain="$_contain"
36
- :wordWrap="$wordwrap"
37
- :maxLines="$maxlines"
38
- :h="$maxheight"
39
- :textAlign="$align"
40
- :overflowSuffix="$textoverflow"
41
- @loaded="$@loaded"
42
- @error="$@error"
43
- />`,
44
- props: [
45
- 'content',
46
- {
47
- key: 'font',
48
- default: Settings.get('defaultFont', 'lato'),
49
- },
50
- {
51
- key: 'size',
52
- cast: Number,
53
- default: 32,
54
- },
55
- 'style',
56
- 'color',
57
- 'weight',
58
- 'letterspacing',
59
- 'stretch',
60
- 'align',
61
- 'wordwrap',
62
- 'maxlines',
63
- 'maxheight',
64
- 'lineheight',
65
- 'contain',
66
- '@loaded',
67
- '@error',
68
- 'textoverflow',
69
- ],
70
- computed: {
71
- _contain() {
72
- return (
73
- this.contain ||
74
- (this.maxheight && this.wordwrap ? 'both' : this.wordwrap ? 'width' : 'none')
75
- )
76
- },
77
- _lineheight() {
78
- return this.lineheight !== undefined ? this.lineheight : this.size
79
- },
80
- },
81
- })