@lightningjs/blits 1.2.0 → 1.3.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 +16 -0
- package/NOTICE +9 -0
- package/docs/built-in/directives.md +4 -6
- package/docs/built-in/for-loop.md +6 -8
- package/docs/components/component_state.md +3 -5
- package/docs/components/computed_properties.md +3 -5
- package/docs/components/lifecycle_events.md +2 -4
- package/docs/components/methods.md +1 -3
- package/docs/components/props.md +3 -5
- package/docs/components/user_input.md +6 -8
- package/docs/components/utilities.md +1 -2
- package/docs/components/watchers.md +2 -4
- package/docs/essentials/application_root.md +1 -3
- package/docs/essentials/components.md +2 -4
- package/docs/essentials/displaying_images.md +4 -6
- package/docs/essentials/displaying_text.md +4 -6
- package/docs/essentials/element_attributes.md +10 -12
- package/docs/essentials/template_syntax.md +5 -7
- package/docs/getting_started/file_structure.md +9 -11
- package/docs/getting_started/getting_started.md +5 -7
- package/docs/plugins/language.md +8 -10
- package/docs/plugins/text-to-speech-announcer.md +2 -4
- package/docs/transitions_animations/transitions.md +5 -7
- package/package.json +1 -1
- package/src/component/base/index.js +2 -3
- package/src/component/base/methods.js +1 -2
- package/src/component/setup/state.js +9 -13
- package/src/component.js +12 -17
- package/src/components/FPScounter.js +56 -12
- package/src/engines/L3/element.js +55 -65
- package/src/lib/codegenerator/generator.js +91 -24
- package/src/lib/codegenerator/generator.test.js +132 -0
- package/src/lib/colors/colors.js +6 -7
- package/src/lib/reactivity/reactive.js +3 -5
- package/src/lib/symbols.js +1 -1
- package/src/lib/templateparser/parser.js +1 -1
- package/src/plugin.js +6 -1
- package/src/{component/base → plugins}/log.js +4 -6
|
@@ -23,7 +23,7 @@ import symbols from '../../lib/symbols.js'
|
|
|
23
23
|
import Settings from '../../settings.js'
|
|
24
24
|
|
|
25
25
|
const isTransition = (value) => {
|
|
26
|
-
return value !== null && typeof value === 'object' && 'transition' in value
|
|
26
|
+
return value !== null && typeof value === 'object' && 'transition' in value === true
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
const isObjectString = (str) => {
|
|
@@ -48,13 +48,12 @@ const parsePercentage = function (v, base) {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
const unpackTransition = (v) => {
|
|
51
|
-
if (v === null) return v
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
else if (typeof v === 'object' && v.constructor === Object) {
|
|
55
|
-
if ('value' in v) {
|
|
51
|
+
if (typeof v !== 'object' || v === null) return v
|
|
52
|
+
if (v.constructor === Object) {
|
|
53
|
+
if ('value' in v === true) {
|
|
56
54
|
return v.value
|
|
57
|
-
}
|
|
55
|
+
}
|
|
56
|
+
if ('transition' in v === true) {
|
|
58
57
|
return unpackTransition(v.transition)
|
|
59
58
|
}
|
|
60
59
|
}
|
|
@@ -68,7 +67,7 @@ const colorMap = {
|
|
|
68
67
|
right: 'colorRight',
|
|
69
68
|
}
|
|
70
69
|
|
|
71
|
-
let textDefaults
|
|
70
|
+
let textDefaults = null
|
|
72
71
|
|
|
73
72
|
const propsTransformer = {
|
|
74
73
|
set parent(v) {
|
|
@@ -102,9 +101,9 @@ const propsTransformer = {
|
|
|
102
101
|
this.props['zIndex'] = v
|
|
103
102
|
},
|
|
104
103
|
set color(v) {
|
|
105
|
-
if (typeof v === 'string' && v.
|
|
104
|
+
if (typeof v === 'string' && v.startsWith('{') === false) {
|
|
106
105
|
this.props['color'] = colors.normalize(v)
|
|
107
|
-
} else if (typeof v === 'object' || (isObjectString(v) && (v = parseToObject(v)))) {
|
|
106
|
+
} else if (typeof v === 'object' || (isObjectString(v) === true && (v = parseToObject(v)))) {
|
|
108
107
|
this.props['color'] = 0
|
|
109
108
|
Object.entries(v).forEach((color) => {
|
|
110
109
|
this.props[colorMap[color[0]]] = colors.normalize(color[1])
|
|
@@ -113,31 +112,31 @@ const propsTransformer = {
|
|
|
113
112
|
},
|
|
114
113
|
set src(v) {
|
|
115
114
|
this.props['src'] = v
|
|
116
|
-
if (this.raw
|
|
115
|
+
if (this.raw['color'] === undefined) {
|
|
117
116
|
this.props['color'] = 0xffffffff
|
|
118
117
|
}
|
|
119
118
|
},
|
|
120
119
|
set texture(v) {
|
|
121
120
|
this.props['texture'] = v
|
|
122
121
|
|
|
123
|
-
if (this.raw
|
|
122
|
+
if (this.raw['color'] === undefined && (v === null || v === undefined)) {
|
|
124
123
|
this.props['color'] = 0x00000000
|
|
125
|
-
} else if (this.raw
|
|
124
|
+
} else if (this.raw['color'] === undefined) {
|
|
126
125
|
this.props['color'] = 0xffffffff
|
|
127
126
|
}
|
|
128
127
|
},
|
|
129
128
|
set rtt(v) {
|
|
130
129
|
this.props['rtt'] = v
|
|
131
|
-
if (v === true && this.raw
|
|
130
|
+
if (v === true && this.raw['color'] === undefined) {
|
|
132
131
|
this.props['color'] = 0xffffffff
|
|
133
132
|
}
|
|
134
133
|
},
|
|
135
134
|
set mount(v) {
|
|
136
|
-
if (typeof v === 'object' || (isObjectString(v) && (v = parseToObject(v)))) {
|
|
137
|
-
if ('x' in v) {
|
|
135
|
+
if (typeof v === 'object' || (isObjectString(v) === true && (v = parseToObject(v)))) {
|
|
136
|
+
if ('x' in v === true) {
|
|
138
137
|
this.props['mountX'] = v.x
|
|
139
138
|
}
|
|
140
|
-
if ('y' in v) {
|
|
139
|
+
if ('y' in v === true) {
|
|
141
140
|
this.props['mountY'] = v.y
|
|
142
141
|
}
|
|
143
142
|
} else {
|
|
@@ -146,11 +145,11 @@ const propsTransformer = {
|
|
|
146
145
|
}
|
|
147
146
|
},
|
|
148
147
|
set pivot(v) {
|
|
149
|
-
if (typeof v === 'object' || (isObjectString(v) && (v = parseToObject(v)))) {
|
|
150
|
-
if ('x' in v) {
|
|
148
|
+
if (typeof v === 'object' || (isObjectString(v) === true && (v = parseToObject(v)))) {
|
|
149
|
+
if ('x' in v === true) {
|
|
151
150
|
this.props['pivotX'] = v.x
|
|
152
151
|
}
|
|
153
|
-
if ('y' in v) {
|
|
152
|
+
if ('y' in v === true) {
|
|
154
153
|
this.props['pivotY'] = v.y
|
|
155
154
|
}
|
|
156
155
|
} else {
|
|
@@ -159,11 +158,11 @@ const propsTransformer = {
|
|
|
159
158
|
}
|
|
160
159
|
},
|
|
161
160
|
set scale(v) {
|
|
162
|
-
if (typeof v === 'object' || (isObjectString(v) && (v = parseToObject(v)))) {
|
|
163
|
-
if ('x' in v) {
|
|
161
|
+
if (typeof v === 'object' || (isObjectString(v) === true && (v = parseToObject(v)))) {
|
|
162
|
+
if ('x' in v === true) {
|
|
164
163
|
this.props['scaleX'] = v.x
|
|
165
164
|
}
|
|
166
|
-
if ('y' in v) {
|
|
165
|
+
if ('y' in v === true) {
|
|
167
166
|
this.props['scaleY'] = v.y
|
|
168
167
|
}
|
|
169
168
|
} else {
|
|
@@ -185,12 +184,7 @@ const propsTransformer = {
|
|
|
185
184
|
},
|
|
186
185
|
set effects(v) {
|
|
187
186
|
this.props['shader'] = renderer.createShader('DynamicShader', {
|
|
188
|
-
effects: v
|
|
189
|
-
if (eff.props && eff.props.color) {
|
|
190
|
-
eff.props.color = colors.normalize(eff.props.color)
|
|
191
|
-
}
|
|
192
|
-
return eff
|
|
193
|
-
}),
|
|
187
|
+
effects: v,
|
|
194
188
|
})
|
|
195
189
|
},
|
|
196
190
|
set clipping(v) {
|
|
@@ -238,24 +232,22 @@ const propsTransformer = {
|
|
|
238
232
|
|
|
239
233
|
const Element = {
|
|
240
234
|
populate(data) {
|
|
241
|
-
const props =
|
|
242
|
-
|
|
243
|
-
...data,
|
|
244
|
-
}
|
|
235
|
+
const props = data
|
|
236
|
+
props['node'] = this.config.node
|
|
245
237
|
|
|
246
|
-
if (props[symbols.isSlot]) {
|
|
238
|
+
if (props[symbols.isSlot] === true) {
|
|
247
239
|
this[symbols.isSlot] = true
|
|
248
240
|
}
|
|
249
241
|
|
|
250
242
|
this.props.element = this
|
|
251
243
|
|
|
252
|
-
|
|
253
|
-
const length = propKeys.length
|
|
254
|
-
|
|
255
|
-
this.props['parent'] = props.parent
|
|
244
|
+
this.props['parent'] = this.config.parent
|
|
256
245
|
delete props.parent
|
|
257
246
|
|
|
258
|
-
this.props.raw =
|
|
247
|
+
this.props.raw = data
|
|
248
|
+
|
|
249
|
+
const propKeys = Object.keys(props)
|
|
250
|
+
const length = propKeys.length
|
|
259
251
|
|
|
260
252
|
for (let i = 0; i < length; i++) {
|
|
261
253
|
const key = propKeys[i]
|
|
@@ -266,7 +258,7 @@ const Element = {
|
|
|
266
258
|
}
|
|
267
259
|
|
|
268
260
|
// correct for default white nodes (but not for text nodes)
|
|
269
|
-
if (this.props.props['color'] === undefined &&
|
|
261
|
+
if (this.props.props['color'] === undefined && '__textnode' in props === false) {
|
|
270
262
|
this.props.props['color'] = 0
|
|
271
263
|
}
|
|
272
264
|
|
|
@@ -274,57 +266,55 @@ const Element = {
|
|
|
274
266
|
? renderer.createTextNode({ ...textDefaults, ...this.props.props })
|
|
275
267
|
: renderer.createNode(this.props.props)
|
|
276
268
|
|
|
277
|
-
if (props['@loaded']) {
|
|
269
|
+
if (props['@loaded'] !== undefined && typeof props['@loaded'] === 'function') {
|
|
278
270
|
this.node.on('loaded', (el, { type, dimensions }) => {
|
|
279
271
|
props['@loaded']({ w: dimensions.width, h: dimensions.height, type }, this)
|
|
280
272
|
})
|
|
281
273
|
}
|
|
282
274
|
|
|
283
|
-
if (props['@error']) {
|
|
275
|
+
if (props['@error'] !== undefined && typeof props['@error'] === 'function') {
|
|
284
276
|
this.node.on('failed', (el, error) => {
|
|
285
277
|
props['@error'](error, this)
|
|
286
278
|
})
|
|
287
279
|
}
|
|
288
280
|
|
|
289
|
-
if (this.component && this.component.___layout) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
281
|
+
// if (this.component && this.component.___layout) {
|
|
282
|
+
// this.node.on('loaded', () => {
|
|
283
|
+
// this.component.___layout()
|
|
284
|
+
// })
|
|
285
|
+
// }
|
|
294
286
|
},
|
|
295
287
|
set(prop, value) {
|
|
296
288
|
if (value === undefined) return
|
|
297
|
-
if (this.props.raw
|
|
298
|
-
|
|
299
|
-
this.props.raw.set(prop, value)
|
|
289
|
+
if (this.props.raw[prop] === value) return
|
|
290
|
+
this.props.raw[prop] = value
|
|
300
291
|
|
|
301
292
|
this.props.props = {}
|
|
302
293
|
this.props[prop] = unpackTransition(value)
|
|
303
|
-
const props = Object.entries(this.props.props)
|
|
304
294
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
295
|
+
const propsKeys = Object.keys(this.props.props)
|
|
296
|
+
|
|
297
|
+
if (propsKeys.length === 1) {
|
|
298
|
+
if (isTransition(value) === true) {
|
|
299
|
+
return this.animate(propsKeys[0], this.props.props[propsKeys[0]], value.transition)
|
|
309
300
|
}
|
|
310
301
|
// set the prop to the value on the node
|
|
311
|
-
this.node[
|
|
302
|
+
this.node[propsKeys[0]] = this.props.props[propsKeys[0]]
|
|
312
303
|
} else {
|
|
313
|
-
for (let i = 0; i <
|
|
304
|
+
for (let i = 0; i < propsKeys.length; i++) {
|
|
314
305
|
// todo: fix code duplication
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
return this.animate(p, v, value.transition)
|
|
306
|
+
if (isTransition(value) === true) {
|
|
307
|
+
return this.animate(propsKeys[i], this.props.props[propsKeys[i]], value.transition)
|
|
318
308
|
}
|
|
319
309
|
// set the prop to the value on the node
|
|
320
|
-
this.node[
|
|
310
|
+
this.node[propsKeys[i]] = this.props.props[propsKeys[i]]
|
|
321
311
|
}
|
|
322
312
|
}
|
|
323
313
|
|
|
324
314
|
// todo: review naming
|
|
325
|
-
if (this.component && this.component.___layout) {
|
|
326
|
-
|
|
327
|
-
}
|
|
315
|
+
// if (this.component && this.component.___layout) {
|
|
316
|
+
// this.component.___layout()
|
|
317
|
+
// }
|
|
328
318
|
},
|
|
329
319
|
animate(prop, value, transition) {
|
|
330
320
|
// if current value is the same as the value to animate to, instantly resolve
|
|
@@ -409,7 +399,7 @@ const Element = {
|
|
|
409
399
|
}
|
|
410
400
|
|
|
411
401
|
export default (config, component) => {
|
|
412
|
-
if (
|
|
402
|
+
if (textDefaults === null) {
|
|
413
403
|
textDefaults = {
|
|
414
404
|
fontSize: 32,
|
|
415
405
|
fontFamily: Settings.get('defaultFont', 'sans-serif'),
|
|
@@ -70,7 +70,7 @@ const generateElementCode = function (
|
|
|
70
70
|
renderCode.push(`const elementConfig${counter} = {}`)
|
|
71
71
|
|
|
72
72
|
if (options.forloop) {
|
|
73
|
-
renderCode.push(`if(
|
|
73
|
+
renderCode.push(`if(${elm} === undefined) {`)
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
renderCode.push(`
|
|
@@ -99,9 +99,20 @@ const generateElementCode = function (
|
|
|
99
99
|
|
|
100
100
|
if (isReactiveKey(key)) {
|
|
101
101
|
if (options.holder && key === ':color') return
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
if (options.holder) {
|
|
103
|
+
this.effectsCode.push(`
|
|
104
|
+
if(typeof skip${counter} === 'undefined' ||
|
|
105
|
+
skip${counter}.indexOf('${key.substring(1)}') === -1)
|
|
106
|
+
${elm}.set('${key.substring(1)}', ${interpolate(templateObject[key], options.component)})
|
|
107
|
+
`)
|
|
108
|
+
} else {
|
|
109
|
+
this.effectsCode.push(`
|
|
110
|
+
${elm}.set('${key.substring(1)}', ${interpolate(
|
|
111
|
+
templateObject[key],
|
|
112
|
+
options.component
|
|
113
|
+
)})
|
|
114
|
+
`)
|
|
115
|
+
}
|
|
105
116
|
renderCode.push(
|
|
106
117
|
`elementConfig${counter}['${key.substring(1)}'] = ${interpolate(
|
|
107
118
|
templateObject[key],
|
|
@@ -117,16 +128,18 @@ const generateElementCode = function (
|
|
|
117
128
|
|
|
118
129
|
if (options.holder === true) {
|
|
119
130
|
renderCode.push(`
|
|
131
|
+
const skip${counter} = []
|
|
120
132
|
if(typeof cmp${counter} !== 'undefined') {
|
|
121
133
|
for(let key in cmp${counter}.config.props) {
|
|
122
134
|
delete elementConfig${counter}[cmp${counter}.config.props[key]]
|
|
135
|
+
skip${counter}.push(cmp${counter}.config.props[key])
|
|
123
136
|
}
|
|
124
137
|
}
|
|
125
138
|
`)
|
|
126
139
|
}
|
|
127
140
|
|
|
128
141
|
if (options.forloop) {
|
|
129
|
-
renderCode.push(`if(
|
|
142
|
+
renderCode.push(`if(${elm}.nodeId === undefined) {`)
|
|
130
143
|
}
|
|
131
144
|
|
|
132
145
|
renderCode.push(`${elm}.populate(elementConfig${counter})`)
|
|
@@ -187,6 +200,11 @@ const generateComponentCode = function (
|
|
|
187
200
|
}
|
|
188
201
|
|
|
189
202
|
renderCode.push(`const props${counter} = {}`)
|
|
203
|
+
|
|
204
|
+
if (options.forloop) {
|
|
205
|
+
renderCode.push(`if(${elm} === undefined) {`)
|
|
206
|
+
}
|
|
207
|
+
|
|
190
208
|
Object.keys(templateObject).forEach((key) => {
|
|
191
209
|
if (isReactiveKey(key)) {
|
|
192
210
|
this.effectsCode.push(`
|
|
@@ -207,10 +225,6 @@ const generateComponentCode = function (
|
|
|
207
225
|
}
|
|
208
226
|
})
|
|
209
227
|
|
|
210
|
-
if (options.forloop) {
|
|
211
|
-
renderCode.push(`if(!${elm}) {`)
|
|
212
|
-
}
|
|
213
|
-
|
|
214
228
|
renderCode.push(`
|
|
215
229
|
componentType = props${counter}['is'] || '${templateObject[Symbol.for('componentType')]}'
|
|
216
230
|
|
|
@@ -256,6 +270,11 @@ const generateForLoopCode = function (templateObject, parent) {
|
|
|
256
270
|
const key = templateObject['key']
|
|
257
271
|
const forKey = interpolate(key, 'scope.')
|
|
258
272
|
|
|
273
|
+
const shallow = !!!(
|
|
274
|
+
templateObject['$shallow'] && templateObject['$shallow'].toLowerCase() === 'false'
|
|
275
|
+
)
|
|
276
|
+
delete templateObject['$shallow']
|
|
277
|
+
|
|
259
278
|
delete templateObject['key']
|
|
260
279
|
const regex = /(.+)\s+in\s+(.+)/gi
|
|
261
280
|
// const regex = /(:?\(*)(.+)\s+in\s+(.+)/gi
|
|
@@ -287,7 +306,12 @@ const generateForLoopCode = function (templateObject, parent) {
|
|
|
287
306
|
const created${forStartCounter} = []
|
|
288
307
|
const forloop${forStartCounter} = (collection = [], elms, created) => {
|
|
289
308
|
const rawCollection = getRaw(collection)
|
|
290
|
-
const keys = new Set(
|
|
309
|
+
const keys = new Set()
|
|
310
|
+
let l = rawCollection.length
|
|
311
|
+
while(l--) {
|
|
312
|
+
const ${item} = rawCollection[l]
|
|
313
|
+
keys.add('' + ${interpolate(key, '') || 'l'})
|
|
314
|
+
}
|
|
291
315
|
`)
|
|
292
316
|
|
|
293
317
|
// keep track of the index in the render code so we can inject
|
|
@@ -297,13 +321,13 @@ const generateForLoopCode = function (templateObject, parent) {
|
|
|
297
321
|
|
|
298
322
|
ctx.renderCode.push(`
|
|
299
323
|
created.length = 0
|
|
300
|
-
const length =
|
|
324
|
+
const length = rawCollection.length
|
|
301
325
|
for(let __index = 0; __index < length; __index++) {
|
|
302
326
|
const scope = Object.create(component)
|
|
303
327
|
parent = ${parent}
|
|
304
328
|
scope['${index}'] = __index
|
|
305
329
|
scope['${item}'] = rawCollection[__index]
|
|
306
|
-
scope['key'] = ${forKey || '__index'}
|
|
330
|
+
scope['key'] = '' + ${forKey || '__index'}
|
|
307
331
|
`)
|
|
308
332
|
if ('ref' in templateObject && templateObject.ref.indexOf('$') === -1) {
|
|
309
333
|
// automatically map the ref for each item in the loop based on the given ref key
|
|
@@ -347,24 +371,33 @@ const generateForLoopCode = function (templateObject, parent) {
|
|
|
347
371
|
(effect) => [...effect.matchAll(scopeRegex)].length !== 0
|
|
348
372
|
)
|
|
349
373
|
|
|
350
|
-
|
|
351
|
-
|
|
374
|
+
if (shallow === false) {
|
|
375
|
+
ctx.renderCode.push(`
|
|
376
|
+
scope['${item}'] = null
|
|
377
|
+
scope['${item}'] = collection[__index]
|
|
352
378
|
`)
|
|
379
|
+
}
|
|
353
380
|
|
|
354
381
|
// inner scope variables are part of the main forloop
|
|
355
382
|
innerScopeEffects.forEach((effect) => {
|
|
356
383
|
const key = effect.indexOf(`scope.${index}`) > -1 ? `'${interpolate(result[2], '')}'` : null
|
|
357
|
-
|
|
358
|
-
|
|
384
|
+
if (effect.indexOf("Symbol.for('props')") === -1) {
|
|
385
|
+
ctx.renderCode.push(`
|
|
386
|
+
effect(() => {
|
|
387
|
+
${effect}
|
|
388
|
+
}, ${key})
|
|
389
|
+
`)
|
|
390
|
+
} else {
|
|
391
|
+
// props shouldn't be wrapped in an effect, but simply passed on
|
|
392
|
+
ctx.renderCode.push(`
|
|
359
393
|
${effect}
|
|
360
|
-
|
|
361
|
-
|
|
394
|
+
`)
|
|
395
|
+
}
|
|
362
396
|
})
|
|
363
|
-
|
|
397
|
+
// if(elms[${forStartCounter}][0] && elms[${forStartCounter}][0].forComponent && elms[${forStartCounter}][0].forComponent.___layout) {
|
|
398
|
+
// elms[${forStartCounter}][0].forComponent.___layout()
|
|
399
|
+
// }
|
|
364
400
|
ctx.renderCode.push(`
|
|
365
|
-
if(elms[${forStartCounter}][0] && elms[${forStartCounter}][0].forComponent && elms[${forStartCounter}][0].forComponent.___layout) {
|
|
366
|
-
elms[${forStartCounter}][0].forComponent.___layout()
|
|
367
|
-
}
|
|
368
401
|
}
|
|
369
402
|
}`)
|
|
370
403
|
|
|
@@ -374,8 +407,8 @@ const generateForLoopCode = function (templateObject, parent) {
|
|
|
374
407
|
let i = created.length
|
|
375
408
|
|
|
376
409
|
while (i--) {
|
|
377
|
-
|
|
378
|
-
|
|
410
|
+
if (keys.has(created[i]) === false) {
|
|
411
|
+
const key = created[i]
|
|
379
412
|
`)
|
|
380
413
|
const forEndCounter = counter
|
|
381
414
|
|
|
@@ -492,6 +525,7 @@ const interpolate = (val, component = 'component.') => {
|
|
|
492
525
|
}
|
|
493
526
|
|
|
494
527
|
const cast = (val = '', key = false, component = 'component.') => {
|
|
528
|
+
const dynamicArgumentRegex = /\$\w+/gi
|
|
495
529
|
let castedValue
|
|
496
530
|
|
|
497
531
|
// inline content
|
|
@@ -542,6 +576,21 @@ const cast = (val = '', key = false, component = 'component.') => {
|
|
|
542
576
|
else if (val.startsWith('$')) {
|
|
543
577
|
castedValue = `${component}${val.replace('$', '')}`
|
|
544
578
|
}
|
|
579
|
+
// dynamic value in object
|
|
580
|
+
else if (dynamicArgumentRegex.exec(val)) {
|
|
581
|
+
const rex = /\w+\s*:\s*(?:[^\s,}]+|".*?"|'.*?')/g
|
|
582
|
+
const results = val.match(rex)
|
|
583
|
+
castedValue = {}
|
|
584
|
+
if (results) {
|
|
585
|
+
for (let i = 0; i < results.length; i++) {
|
|
586
|
+
const members = results[i].split(/\s*:\s*/)
|
|
587
|
+
if (members) {
|
|
588
|
+
populateFields(members[0], members[1], component, castedValue)
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
return interpolateObject(castedValue)
|
|
593
|
+
}
|
|
545
594
|
// static string
|
|
546
595
|
else {
|
|
547
596
|
castedValue = `"${val}"`
|
|
@@ -563,3 +612,21 @@ const parseInlineContent = (val, component) => {
|
|
|
563
612
|
}
|
|
564
613
|
return val
|
|
565
614
|
}
|
|
615
|
+
|
|
616
|
+
// Populate value into target's prop
|
|
617
|
+
const populateFields = (prop, val, component, target) => {
|
|
618
|
+
if (val.startsWith('$')) {
|
|
619
|
+
target[prop] = `${component}${val.replace('$', '')}`
|
|
620
|
+
} else {
|
|
621
|
+
target[prop] = val
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
const interpolateObject = (input) => {
|
|
626
|
+
const interpolatedResults = []
|
|
627
|
+
Object.keys(input).forEach((key) => {
|
|
628
|
+
interpolatedResults.push(`${key}: ${input[key]}`)
|
|
629
|
+
})
|
|
630
|
+
|
|
631
|
+
return ` { ${interpolatedResults.join(', ')} }`
|
|
632
|
+
}
|
|
@@ -684,6 +684,138 @@ test('Generate code for a template with dynamic attributes with code to be evalu
|
|
|
684
684
|
assert.end()
|
|
685
685
|
})
|
|
686
686
|
|
|
687
|
+
test('Generate code for a template with attribute (object)', (assert) => {
|
|
688
|
+
const templateObject = {
|
|
689
|
+
children: [
|
|
690
|
+
{
|
|
691
|
+
[Symbol.for('componentType')]: 'Element',
|
|
692
|
+
w: '100',
|
|
693
|
+
h: '100',
|
|
694
|
+
color: '{top: 0xff0000ff, bottom: 0x008000ff, left: 0xffff00ff }',
|
|
695
|
+
},
|
|
696
|
+
],
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
const expectedRender = `
|
|
700
|
+
function anonymous(parent,component,context,components,effect,getRaw) {
|
|
701
|
+
const elms = []
|
|
702
|
+
let componentType
|
|
703
|
+
const rootComponent = component
|
|
704
|
+
const elementConfig0 = {}
|
|
705
|
+
|
|
706
|
+
elms[0] = this.element({parent: parent || 'root'}, component)
|
|
707
|
+
elementConfig0['w'] = 100
|
|
708
|
+
elementConfig0['h'] = 100
|
|
709
|
+
elementConfig0['color'] = "{top: 0xff0000ff, bottom: 0x008000ff, left: 0xffff00ff}"
|
|
710
|
+
elms[0].populate(elementConfig0)
|
|
711
|
+
|
|
712
|
+
return elms
|
|
713
|
+
}
|
|
714
|
+
`
|
|
715
|
+
|
|
716
|
+
const actual = generator.call(scope, templateObject)
|
|
717
|
+
console.log(actual.render.toString())
|
|
718
|
+
assert.equal(
|
|
719
|
+
normalize(actual.render.toString()),
|
|
720
|
+
normalize(expectedRender),
|
|
721
|
+
'Generator should return a render function with the correct code'
|
|
722
|
+
)
|
|
723
|
+
assert.ok(
|
|
724
|
+
Array.isArray(actual.effects) && actual.effects.length === 0,
|
|
725
|
+
'Generator should return an empty effects array'
|
|
726
|
+
)
|
|
727
|
+
|
|
728
|
+
assert.end()
|
|
729
|
+
})
|
|
730
|
+
|
|
731
|
+
test('Generate code for a template with dynamic attribute (object)', (assert) => {
|
|
732
|
+
const templateObject = {
|
|
733
|
+
children: [
|
|
734
|
+
{
|
|
735
|
+
[Symbol.for('componentType')]: 'Element',
|
|
736
|
+
w: '100',
|
|
737
|
+
h: '100',
|
|
738
|
+
color: '{top: $colorT, bottom: $colorB, left:$colorL}',
|
|
739
|
+
},
|
|
740
|
+
],
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
const expectedRender = `
|
|
744
|
+
function anonymous(parent,component,context,components,effect,getRaw) {
|
|
745
|
+
const elms = []
|
|
746
|
+
let componentType
|
|
747
|
+
const rootComponent = component
|
|
748
|
+
const elementConfig0 = {}
|
|
749
|
+
|
|
750
|
+
elms[0] = this.element({parent: parent || 'root'}, component)
|
|
751
|
+
elementConfig0['w'] = 100
|
|
752
|
+
elementConfig0['h'] = 100
|
|
753
|
+
elementConfig0['color'] = { top: component.colorT, bottom: component.colorB, left: component.colorL }
|
|
754
|
+
elms[0].populate(elementConfig0)
|
|
755
|
+
|
|
756
|
+
return elms
|
|
757
|
+
}
|
|
758
|
+
`
|
|
759
|
+
|
|
760
|
+
const actual = generator.call(scope, templateObject)
|
|
761
|
+
|
|
762
|
+
assert.equal(
|
|
763
|
+
normalize(actual.render.toString()),
|
|
764
|
+
normalize(expectedRender),
|
|
765
|
+
'Generator should return a render function with the correct code'
|
|
766
|
+
)
|
|
767
|
+
assert.ok(
|
|
768
|
+
Array.isArray(actual.effects) && actual.effects.length === 0,
|
|
769
|
+
'Generator should return an empty effects array'
|
|
770
|
+
)
|
|
771
|
+
|
|
772
|
+
assert.end()
|
|
773
|
+
})
|
|
774
|
+
|
|
775
|
+
test('Generate code for a template with attribute (object) with mixed dynamic & static value', (assert) => {
|
|
776
|
+
const templateObject = {
|
|
777
|
+
children: [
|
|
778
|
+
{
|
|
779
|
+
[Symbol.for('componentType')]: 'Element',
|
|
780
|
+
w: '100',
|
|
781
|
+
h: '100',
|
|
782
|
+
color: '{top: 0xff0000ff, bottom: $colorB, left: $colorL}',
|
|
783
|
+
},
|
|
784
|
+
],
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
const expectedRender = `
|
|
788
|
+
function anonymous(parent,component,context,components,effect,getRaw) {
|
|
789
|
+
const elms = []
|
|
790
|
+
let componentType
|
|
791
|
+
const rootComponent = component
|
|
792
|
+
const elementConfig0 = {}
|
|
793
|
+
|
|
794
|
+
elms[0] = this.element({parent: parent || 'root'}, component)
|
|
795
|
+
elementConfig0['w'] = 100
|
|
796
|
+
elementConfig0['h'] = 100
|
|
797
|
+
elementConfig0['color'] = { top: 0xff0000ff, bottom: component.colorB, left: component.colorL }
|
|
798
|
+
elms[0].populate(elementConfig0)
|
|
799
|
+
|
|
800
|
+
return elms
|
|
801
|
+
}
|
|
802
|
+
`
|
|
803
|
+
|
|
804
|
+
const actual = generator.call(scope, templateObject)
|
|
805
|
+
|
|
806
|
+
assert.equal(
|
|
807
|
+
normalize(actual.render.toString()),
|
|
808
|
+
normalize(expectedRender),
|
|
809
|
+
'Generator should return a render function with the correct code'
|
|
810
|
+
)
|
|
811
|
+
assert.ok(
|
|
812
|
+
Array.isArray(actual.effects) && actual.effects.length === 0,
|
|
813
|
+
'Generator should return an empty effects array'
|
|
814
|
+
)
|
|
815
|
+
|
|
816
|
+
assert.end()
|
|
817
|
+
})
|
|
818
|
+
|
|
687
819
|
test('Generate code for a template with @-listeners', (assert) => {
|
|
688
820
|
const templateObject = {
|
|
689
821
|
children: [
|
package/src/lib/colors/colors.js
CHANGED
|
@@ -39,17 +39,16 @@ export default {
|
|
|
39
39
|
normalize: (color = '', defaultColor = '0xffffffff') => {
|
|
40
40
|
color = color.toString()
|
|
41
41
|
|
|
42
|
-
//
|
|
43
|
-
if (colorsMap[color] !== undefined) {
|
|
44
|
-
return colorsMap[color]
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// its a 0xRRGGBBAA color, store it and return it
|
|
42
|
+
// it is a 0xRRGGBBAA color, return it
|
|
48
43
|
if (color.startsWith('0x') && color.length === 10) {
|
|
49
|
-
colorsMap[color] = color
|
|
50
44
|
return color
|
|
51
45
|
}
|
|
52
46
|
|
|
47
|
+
// it is a previously mapped color, return it
|
|
48
|
+
if (colorsMap[color] !== undefined) {
|
|
49
|
+
return colorsMap[color]
|
|
50
|
+
}
|
|
51
|
+
|
|
53
52
|
// check for hex color
|
|
54
53
|
if (hex.test(color)) {
|
|
55
54
|
color = color.replace('#', '').toLowerCase()
|
|
@@ -30,11 +30,9 @@ export const getRaw = (value) => {
|
|
|
30
30
|
const reactiveProxy = (original, _parent = null, _key) => {
|
|
31
31
|
// don't create a proxy when a Blits component or an Image Texture
|
|
32
32
|
// is assigned to a state variable
|
|
33
|
-
if (
|
|
34
|
-
(
|
|
35
|
-
original.constructor.name === '_ImageTexture'
|
|
36
|
-
) {
|
|
37
|
-
return original
|
|
33
|
+
if (typeof original === 'object') {
|
|
34
|
+
if (original[symbols.id] !== undefined) return original
|
|
35
|
+
if (original.constructor.name === '_ImageTexture') return original
|
|
38
36
|
}
|
|
39
37
|
|
|
40
38
|
// if original object is already a proxy, don't create a new one but return the existing one instead
|
package/src/lib/symbols.js
CHANGED
|
@@ -11,10 +11,10 @@ export default {
|
|
|
11
11
|
init: Symbol('init'),
|
|
12
12
|
inputEvents: Symbol('inputEvents'),
|
|
13
13
|
intervals: Symbol('intervals'),
|
|
14
|
+
launched: Symbol('launched'),
|
|
14
15
|
level: Symbol('level'),
|
|
15
16
|
methodKeys: Symbol('methodKeys'),
|
|
16
17
|
originalState: Symbol('originalState'),
|
|
17
|
-
pluginsRegistered: Symbol('pluginsRegistered'),
|
|
18
18
|
propKeys: Symbol('propKeys'),
|
|
19
19
|
raw: Symbol('raw'),
|
|
20
20
|
ready: Symbol('ready'),
|
|
@@ -28,7 +28,7 @@ export default (template = '', componentName, parentComponent, filePath = null)
|
|
|
28
28
|
// regular expressions
|
|
29
29
|
const tagStartRegex = /^<\/?([a-zA-Z0-9_\-.]+)\s*/
|
|
30
30
|
const tagEndRegex = /^\s*(\/?>)\s*/
|
|
31
|
-
const attrNameRegex = /^([A-Za-z0-9:.\-_
|
|
31
|
+
const attrNameRegex = /^([A-Za-z0-9:.\-_@$]+)=\s*(["'])/
|
|
32
32
|
const emptyTagStartRegex = /^<>\s*/
|
|
33
33
|
const emptyTagEndRegex = /^\s*(<\/>)\s*/
|
|
34
34
|
|