@lightningjs/blits 0.9.9 → 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 (45) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/boilerplate/common/public/fonts/Lato-Regular.ttf +0 -0
  3. package/boilerplate/common/public/fonts/Raleway-ExtraBold.ttf +0 -0
  4. package/docs/essentials/displaying_text.md +1 -1
  5. package/package.json +3 -4
  6. package/src/component/base/events.js +1 -1
  7. package/src/component/base/utils.js +34 -1
  8. package/src/component/setup/props.js +3 -1
  9. package/src/component/setup/props.test.js +51 -19
  10. package/src/component.d.ts +2 -1
  11. package/src/component.js +20 -13
  12. package/src/components/Circle.js +13 -12
  13. package/src/components/FPScounter.js +413 -465
  14. package/src/components/Image.js +7 -9
  15. package/src/components/Layout.js +49 -0
  16. package/src/components/Layout.orig.js +34 -0
  17. package/src/components/RouterView.js +6 -10
  18. package/src/components/Sprite.js +10 -12
  19. package/src/components/index.js +2 -2
  20. package/src/components/index.orig.js +2 -2
  21. package/src/engine.js +1 -1
  22. package/src/engines/L3/element.js +193 -180
  23. package/src/engines/L3/element.test.js +170 -0
  24. package/src/engines/L3/fontLoader.js +20 -7
  25. package/src/engines/L3/index.js +2 -2
  26. package/src/engines/L3/launch.js +7 -13
  27. package/src/engines/L3/shaderLoader.js +12 -0
  28. package/src/engines/index.js +1 -1
  29. package/src/launch.d.ts +19 -2
  30. package/src/lib/codegenerator/generator.js +214 -77
  31. package/src/lib/codegenerator/generator.test.js +657 -942
  32. package/src/lib/colors/colors.js +59 -46
  33. package/src/lib/precompiler/precompiler.js +2 -1
  34. package/src/lib/reactivity/effect.js +10 -13
  35. package/src/lib/reactivity/reactive.js +24 -14
  36. package/src/lib/symbols.js +1 -0
  37. package/src/router/router.js +17 -3
  38. package/vite/msdfGenerator.js +52 -53
  39. package/boilerplate/common/public/fonts/Lato-Regular.msdf.json +0 -5383
  40. package/boilerplate/common/public/fonts/Lato-Regular.msdf.png +0 -0
  41. package/boilerplate/common/public/fonts/Raleway-ExtraBold.msdf.json +0 -10838
  42. package/boilerplate/common/public/fonts/Raleway-ExtraBold.msdf.png +0 -0
  43. package/boilerplate/js/default/src/renderer.js +0 -4
  44. package/src/components/Text.js +0 -119
  45. package/src/components/Text.orig.js +0 -81
@@ -19,7 +19,7 @@ let counter
19
19
 
20
20
  export default function (templateObject = { children: [] }) {
21
21
  const ctx = {
22
- renderCode: ['const elms = []'],
22
+ renderCode: ['const elms = []', 'let componentType', 'const rootComponent = component'],
23
23
  effectsCode: [],
24
24
  context: { props: [], components: this.components },
25
25
  }
@@ -29,9 +29,18 @@ export default function (templateObject = { children: [] }) {
29
29
  ctx.renderCode.push('return elms')
30
30
 
31
31
  return {
32
- render: new Function('parent', 'component', 'context', 'components', ctx.renderCode.join('\n')),
32
+ render: new Function(
33
+ 'parent',
34
+ 'component',
35
+ 'context',
36
+ 'components',
37
+ 'effect',
38
+ 'getRaw',
39
+ ctx.renderCode.join('\n')
40
+ ),
33
41
  effects: ctx.effectsCode.map(
34
- (code) => new Function('component', 'elms', 'context', 'components', code)
42
+ (code) =>
43
+ new Function('component', 'elms', 'context', 'components', 'rootComponent', 'effect', code)
35
44
  ),
36
45
  context: ctx.context,
37
46
  }
@@ -40,7 +49,7 @@ export default function (templateObject = { children: [] }) {
40
49
  const generateElementCode = function (
41
50
  templateObject,
42
51
  parent = false,
43
- options = { key: false, component: 'component.', forceEffect: false }
52
+ options = { key: false, component: 'component.', forceEffect: false, forloop: false }
44
53
  ) {
45
54
  const renderCode = options.forceEffect ? this.effectsCode : this.renderCode
46
55
  if (parent) {
@@ -52,17 +61,26 @@ const generateElementCode = function (
52
61
 
53
62
  if (options.key) {
54
63
  renderCode.push(`
55
- elms[${counter}] = elms[${counter}] || {}
64
+ if(elms[${counter}] === undefined) {
65
+ elms[${counter}] = {}
66
+ }
56
67
  `)
57
68
  }
58
69
 
70
+ renderCode.push(`const elementConfig${counter} = {}`)
71
+
72
+ if (options.forloop) {
73
+ renderCode.push(`if(!${elm}) {`)
74
+ }
75
+
59
76
  renderCode.push(`
60
- if(!${elm}) {
61
- ${elm} = this.element({parent: parent || 'root'}, component)
62
- }
63
- const elementConfig${counter} = {}
77
+ ${elm} = this.element({parent: parent || 'root'}, component)
64
78
  `)
65
79
 
80
+ if (options.forloop) {
81
+ renderCode.push('}')
82
+ }
83
+
66
84
  const children = templateObject['children']
67
85
  delete templateObject['children']
68
86
 
@@ -77,11 +95,19 @@ const generateElementCode = function (
77
95
  `)
78
96
  }
79
97
 
98
+ if (key === 'key') return
99
+
80
100
  if (isReactiveKey(key)) {
81
101
  if (options.holder && key === ':color') return
82
102
  this.effectsCode.push(
83
103
  `${elm}.set('${key.substring(1)}', ${interpolate(templateObject[key], options.component)})`
84
104
  )
105
+ renderCode.push(
106
+ `elementConfig${counter}['${key.substring(1)}'] = ${interpolate(
107
+ templateObject[key],
108
+ options.component
109
+ )}`
110
+ )
85
111
  } else {
86
112
  renderCode.push(
87
113
  `elementConfig${counter}['${key}'] = ${cast(templateObject[key], key, options.component)}`
@@ -99,11 +125,15 @@ const generateElementCode = function (
99
125
  `)
100
126
  }
101
127
 
102
- renderCode.push(`
103
- if(!${elm}.nodeId) {
104
- ${elm}.populate(elementConfig${counter})
105
- }
106
- `)
128
+ if (options.forloop) {
129
+ renderCode.push(`if(!${elm}.nodeId) {`)
130
+ }
131
+
132
+ renderCode.push(`${elm}.populate(elementConfig${counter})`)
133
+
134
+ if (options.forloop) {
135
+ renderCode.push('}')
136
+ }
107
137
 
108
138
  if (children) {
109
139
  generateCode.call(this, { children }, `${elm}`, options)
@@ -113,7 +143,13 @@ const generateElementCode = function (
113
143
  const generateComponentCode = function (
114
144
  templateObject,
115
145
  parent = false,
116
- options = { key: false, component: 'component.', forceEffect: false, holder: false }
146
+ options = {
147
+ key: false,
148
+ component: 'component.',
149
+ forceEffect: false,
150
+ holder: false,
151
+ forloop: false,
152
+ }
117
153
  ) {
118
154
  const renderCode = options.forceEffect ? this.effectsCode : this.renderCode
119
155
 
@@ -140,7 +176,9 @@ const generateComponentCode = function (
140
176
 
141
177
  if (options.key) {
142
178
  renderCode.push(`
143
- elms[${counter}] = elms[${counter}] || {}
179
+ if(elms[${counter}] === undefined) {
180
+ elms[${counter}] = {}
181
+ }
144
182
  `)
145
183
  }
146
184
 
@@ -169,34 +207,56 @@ const generateComponentCode = function (
169
207
  }
170
208
  })
171
209
 
210
+ if (options.forloop) {
211
+ renderCode.push(`if(!${elm}) {`)
212
+ }
213
+
172
214
  renderCode.push(`
173
- if(!${elm}) {
174
- const componentType = props${counter}['is'] || '${
175
- templateObject[Symbol.for('componentType')]
176
- }'
177
-
178
- ${elm} = (context.components && context.components[componentType] || components[componentType] || (() => { console.error('component ${
179
- templateObject[Symbol.for('componentType')]
180
- } not found')})).call(null, {props: props${counter}}, ${parent}, component)
181
- if (${elm}[Symbol.for('slots')][0]) {
182
- parent = ${elm}[Symbol.for('slots')][0]
183
- component = ${elm}
184
- } else {
185
- parent = ${elm}[Symbol.for('children')][0]
215
+ componentType = props${counter}['is'] || '${templateObject[Symbol.for('componentType')]}'
216
+
217
+ let component${counter}
218
+ if(typeof componentType === 'string') {
219
+ component${counter} = context.components && context.components[componentType] || components[componentType]
220
+ if(!component${counter}) {
221
+ throw new Error('Component "${templateObject[Symbol.for('componentType')]}" not found')
186
222
  }
223
+ } else if(typeof componentType === 'function' && componentType.name === 'factory') {
224
+ component${counter} = componentType
225
+ }
226
+
227
+ ${elm} = component${counter}.call(null, {props: props${counter}}, ${parent}, component)
228
+
229
+ if (${elm}[Symbol.for('slots')][0]) {
230
+ parent = ${elm}[Symbol.for('slots')][0]
231
+ component = ${elm}
232
+ } else {
233
+ parent = ${elm}[Symbol.for('children')][0]
187
234
  }
188
235
  `)
189
236
 
237
+ if (options.forloop) {
238
+ renderCode.push('}')
239
+ }
240
+
190
241
  if (children) {
191
242
  counter++
192
243
  generateElementCode.call(this, { children }, false, { ...options })
193
244
  }
245
+ // if (!options.forloop) {
246
+ // renderCode.push(`
247
+ // component = rootComponent
248
+ // `)
249
+ // }
194
250
  }
195
251
 
196
252
  const generateForLoopCode = function (templateObject, parent) {
197
253
  const forLoop = templateObject[':for']
198
254
  delete templateObject[':for']
199
255
 
256
+ const key = templateObject['key']
257
+ const forKey = interpolate(key, 'scope.')
258
+
259
+ delete templateObject['key']
200
260
  const regex = /(.+)\s+in\s+(.+)/gi
201
261
  // const regex = /(:?\(*)(.+)\s+in\s+(.+)/gi
202
262
 
@@ -208,6 +268,8 @@ const generateForLoopCode = function (templateObject, parent) {
208
268
  .replace(')', '')
209
269
  .split(/\s*,\s*/)
210
270
 
271
+ const scopeRegex = new RegExp(`(scope\\.(?!${item}\\.|${index}|key)(\\w+))`, 'gi')
272
+
211
273
  // local context
212
274
  const ctx = {
213
275
  renderCode: [],
@@ -216,87 +278,157 @@ const generateForLoopCode = function (templateObject, parent) {
216
278
  }
217
279
 
218
280
  if (parent) {
219
- ctx.renderCode.push(`let parent = ${parent}`)
281
+ ctx.renderCode.push(`parent = ${parent}`)
220
282
  }
221
283
 
222
284
  const forStartCounter = counter
223
285
 
224
- // reference all reactive attributes (except those referencing the iterator item),
225
- // to ensure that reactivity is registered, even when the initial array is empty
226
- Object.keys(templateObject).forEach((k) => {
227
- if (isReactiveKey(k) && templateObject[k].indexOf('$' + item) === -1) {
228
- ctx.renderCode.push(`
229
- void ${interpolate(templateObject[k], 'component.')}
230
- `)
231
- }
232
- })
233
-
234
286
  ctx.renderCode.push(`
235
- const collection = ${cast(result[2], ':for')} || []
236
- const keys = []
237
- for(let __index = 0; __index < collection.length; __index++) {
238
- parent = ${parent}
239
- if(!component.key) keys.length = 0
240
- const scope = Object.assign(component, {
241
- key: Math.random().toString(),
242
- ${index}: __index,
243
- ${item}: collection[__index],
244
- `)
287
+ const created${forStartCounter} = []
288
+ const forloop${forStartCounter} = (collection = [], elms, created) => {
289
+ const rawCollection = getRaw(collection)
290
+ const keys = new Set(rawCollection.map((${item}) => '' + ${interpolate(key, '')}))
291
+ `)
292
+
293
+ // keep track of the index in the render code so we can inject
294
+ // the code that takes care of destroying elements (generated later on)
295
+ // in the right spot
296
+ const indexToInjectDestroyCode = ctx.renderCode.length
245
297
 
298
+ ctx.renderCode.push(`
299
+ created.length = 0
300
+ const length = collection.length
301
+ for(let __index = 0; __index < length; __index++) {
302
+ const scope = Object.create(component)
303
+ parent = ${parent}
304
+ scope['${index}'] = __index
305
+ scope['${item}'] = rawCollection[__index]
306
+ scope['key'] = ${forKey || '__index'}
307
+ `)
246
308
  if ('ref' in templateObject && templateObject.ref.indexOf('$') === -1) {
247
309
  // automatically map the ref for each item in the loop based on the given ref key
248
310
  ctx.renderCode.push(`
249
- __ref: '${templateObject.ref}' + __index
311
+ scope['__ref'] = '${templateObject.ref}' + __index
250
312
  `)
251
313
  templateObject.ref = '$__ref'
252
314
  }
315
+
253
316
  ctx.renderCode.push(`
254
- })
317
+ created.push(scope.key)
255
318
  `)
256
319
 
257
- if ('key' in templateObject) {
258
- ctx.renderCode.push(`
259
- keys.push(${interpolate(templateObject.key, 'scope.')}.toString())
260
- `)
261
- } else {
262
- ctx.renderCode.push(`
263
- keys.push(scope.key.toString())
264
- `)
265
- }
266
-
267
320
  if (
268
321
  templateObject[Symbol.for('componentType')] === 'Element' ||
269
- templateObject[Symbol.for('componentType')] === 'Slot'
322
+ templateObject[Symbol.for('componentType')] === 'Slot' ||
323
+ templateObject[Symbol.for('componentType')] === 'Text'
270
324
  ) {
271
325
  generateElementCode.call(ctx, templateObject, parent, {
272
326
  key: 'scope.key',
273
327
  component: 'scope.',
274
328
  forceEffect: false,
329
+ forloop: true,
275
330
  })
276
331
  } else {
277
332
  generateComponentCode.call(ctx, templateObject, false, {
278
333
  key: 'scope.key',
279
334
  component: 'scope.',
280
335
  forceEffect: false,
336
+ forloop: true,
281
337
  })
282
338
  }
283
- ctx.renderCode = ctx.renderCode.concat(ctx.effectsCode)
284
- ctx.renderCode.push('}')
285
339
 
340
+ // separate effects that only rely on variables in the itteration
341
+ const innerScopeEffects = ctx.effectsCode.filter(
342
+ (effect) => [...effect.matchAll(scopeRegex)].length === 0
343
+ )
344
+
345
+ // separate effects that (also) rely on variables in the outer scope
346
+ const outerScopeEffects = ctx.effectsCode.filter(
347
+ (effect) => [...effect.matchAll(scopeRegex)].length !== 0
348
+ )
349
+
350
+ ctx.renderCode.push(`
351
+ scope['${item}'] = collection[__index]
352
+ `)
353
+
354
+ // inner scope variables are part of the main forloop
355
+ innerScopeEffects.forEach((effect) => {
356
+ const key = effect.indexOf(`scope.${index}`) > -1 ? `'${interpolate(result[2], '')}'` : null
357
+ ctx.renderCode.push(`
358
+ effect(() => {
359
+ ${effect}
360
+ }, ${key})
361
+ `)
362
+ })
363
+
364
+ 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
+ }
369
+ }`)
370
+
371
+ // generate code that destroys items
372
+ const destroyCode = []
373
+ destroyCode.push(`
374
+ let i = created.length
375
+
376
+ while (i--) {
377
+ const key = created[i]
378
+ if (!keys.has(key)) {
379
+ `)
286
380
  const forEndCounter = counter
381
+
382
+ for (let i = forStartCounter; i <= forEndCounter; i++) {
383
+ destroyCode.push(`
384
+ elms[${i}][key] && elms[${i}][key].destroy()
385
+ delete elms[${i}][key]
386
+ `)
387
+ }
388
+ destroyCode.push(`
389
+ }
390
+ }
391
+ `)
392
+
393
+ // inject the destroy code in the correct spot
394
+ ctx.renderCode.splice(indexToInjectDestroyCode, 0, ...destroyCode)
287
395
  ctx.renderCode.push(`
288
- if(elms[${forStartCounter}]) {
289
- Object.keys(elms[${forStartCounter}]).forEach(key => {
290
- if(keys.indexOf(key) === -1) {
291
- for(let i=${forStartCounter}; i <= ${forEndCounter}; i++){
292
- elms[i][key].destroy && elms[i][key].destroy()
293
- delete elms[i][key]
294
- }
396
+ effect(() => {
397
+ forloop${forStartCounter}(${cast(result[2], ':for')}, elms, created${forStartCounter})
398
+ }, '${interpolate(result[2], '')}')
399
+ `)
400
+
401
+ outerScopeEffects.forEach((effect) => {
402
+ const matches = [...effect.matchAll(scopeRegex)]
403
+
404
+ let l = matches.length
405
+ const refs = []
406
+ while (l--) {
407
+ const match = matches[l]
408
+ const ref = `component.${match[2]}`
409
+ refs.indexOf(ref) === -1 && refs.push(ref)
410
+ effect =
411
+ effect.substring(0, match.index) + ref + effect.substring(match.index + match[1].length)
412
+ }
413
+
414
+ ctx.renderCode.push(`
415
+ effect(() => {
416
+ void ${refs.join(', ')}
417
+ for(let __index = 0; __index < ${interpolate(result[2])}.length; __index++) {
418
+ const scope = {}
419
+ scope['${index}'] = __index
420
+ scope['${item}'] = ${interpolate(result[2])}[__index]
421
+ scope['key'] = ${forKey || '__index'}
422
+ `)
423
+
424
+ ctx.renderCode.push(`
425
+ ${effect}
295
426
  }
296
427
  })
297
- }
298
- `)
299
- this.effectsCode.push(ctx.renderCode.join('\n'))
428
+ `)
429
+ })
430
+
431
+ this.renderCode.push(ctx.renderCode.join('\n'))
300
432
  }
301
433
 
302
434
  const generateCode = function (templateObject, parent = false, options = {}) {
@@ -309,8 +441,12 @@ const generateCode = function (templateObject, parent = false, options = {}) {
309
441
  } else {
310
442
  if (
311
443
  childTemplateObject[Symbol.for('componentType')] === 'Element' ||
312
- childTemplateObject[Symbol.for('componentType')] === 'Slot'
444
+ childTemplateObject[Symbol.for('componentType')] === 'Slot' ||
445
+ childTemplateObject[Symbol.for('componentType')] === 'Text'
313
446
  ) {
447
+ if (childTemplateObject[Symbol.for('componentType')] === 'Text') {
448
+ childTemplateObject.__textnode = 'true'
449
+ }
314
450
  generateElementCode.call(this, childTemplateObject, parent, options)
315
451
  } else {
316
452
  generateComponentCode.call(this, childTemplateObject, parent, options)
@@ -320,6 +456,7 @@ const generateCode = function (templateObject, parent = false, options = {}) {
320
456
  }
321
457
 
322
458
  const interpolate = (val, component = 'component.') => {
459
+ if (val === undefined) return val
323
460
  const replaceString = /('.*?')+/gi
324
461
  const replaceDollar = /\$/gi
325
462
  const matches = val.matchAll(replaceString)
@@ -335,7 +472,7 @@ const interpolate = (val, component = 'component.') => {
335
472
  }
336
473
 
337
474
  // replace remaining dollar signs
338
- val = val.replace(replaceDollar, component)
475
+ val = val.replace(replaceDollar, component || '')
339
476
 
340
477
  // restore string replacemenet
341
478
  restore.forEach((el, idx) => {