@lightningjs/blits 1.14.1 → 1.16.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.
@@ -29,13 +29,24 @@ export default () =>
29
29
  let componentType
30
30
  const rootComponent = component
31
31
  let propData
32
+ let slotComponent
33
+ let inSlot = false
34
+ let slotChildCounter = 0
32
35
  const elementConfig0 = {}
33
36
 
34
- elms[0] = this.element({ parent: parent || 'root' }, component)
37
+ elms[0] = this.element(
38
+ { parent: parent || 'root' },
39
+ inSlot === true ? slotComponent : component
40
+ )
35
41
 
36
42
  elementConfig0['w'] = parent.node.width * (100 / 100)
37
43
  elementConfig0['height'] = parent.node.height * (100 / 100)
38
44
  elms[0].populate(elementConfig0)
45
+
46
+ if (inSlot === true) {
47
+ slotChildCounter -= 1
48
+ }
49
+
39
50
  return elms
40
51
  },
41
52
  effects: [],
@@ -28,15 +28,26 @@ export default () =>
28
28
  let componentType
29
29
  const rootComponent = component
30
30
  let propData
31
+ let slotComponent
32
+ let inSlot = false
33
+ let slotChildCounter = 0
31
34
  const elementConfig0 = {}
32
35
 
33
- elms[0] = this.element({ parent: parent || 'root' }, component)
36
+ elms[0] = this.element(
37
+ { parent: parent || 'root' },
38
+ inSlot === true ? slotComponent : component
39
+ )
34
40
 
35
41
  elementConfig0['w'] = parent.node.width * (100 / 100)
36
42
  elementConfig0['h'] = parent.node.height * (100 / 100)
37
43
  elementConfig0['texture'] = component.texture
38
44
  elementConfig0['color'] = component.color
39
45
  elms[0].populate(elementConfig0)
46
+
47
+ if (inSlot === true) {
48
+ slotChildCounter -= 1
49
+ }
50
+
40
51
  return elms
41
52
  },
42
53
  effects: [
@@ -52,7 +63,7 @@ export default () =>
52
63
  props: ['image', 'map', 'frame', 'color'],
53
64
  state() {
54
65
  return {
55
- spriteTexture: false,
66
+ spriteTexture: null,
56
67
  }
57
68
  },
58
69
  computed: {
@@ -62,7 +73,7 @@ export default () =>
62
73
  ? Object.assign({}, this.map.defaults || {}, this.map.frames[this.frame])
63
74
  : this.map[this.frame]
64
75
 
65
- if (this.spriteTexture && options) {
76
+ if (this.spriteTexture !== null && options) {
66
77
  return this[symbols.renderer]().createTexture('SubTexture', {
67
78
  texture: this.spriteTexture,
68
79
  x: options.x,
@@ -74,7 +85,7 @@ export default () =>
74
85
  },
75
86
  },
76
87
  hooks: {
77
- ready() {
88
+ init() {
78
89
  this.spriteTexture = this[symbols.renderer]().createTexture('ImageTexture', {
79
90
  src: this.image,
80
91
  })
@@ -27,7 +27,7 @@ export default () =>
27
27
  props: ['image', 'map', 'frame', 'color'],
28
28
  state() {
29
29
  return {
30
- spriteTexture: false,
30
+ spriteTexture: null,
31
31
  }
32
32
  },
33
33
  computed: {
@@ -37,7 +37,7 @@ export default () =>
37
37
  ? Object.assign({}, this.map.defaults || {}, this.map.frames[this.frame])
38
38
  : this.map[this.frame]
39
39
 
40
- if (this.spriteTexture && options) {
40
+ if (this.spriteTexture !== null && options) {
41
41
  return this[symbols.renderer]().createTexture('SubTexture', {
42
42
  texture: this.spriteTexture,
43
43
  x: options.x,
@@ -49,7 +49,7 @@ export default () =>
49
49
  },
50
50
  },
51
51
  hooks: {
52
- ready() {
52
+ init() {
53
53
  this.spriteTexture = this[symbols.renderer]().createTexture('ImageTexture', {
54
54
  src: this.image,
55
55
  })
@@ -383,6 +383,37 @@ const propsTransformer = {
383
383
  set content(v) {
384
384
  this.props['text'] = '' + v
385
385
  },
386
+ set placement(v) {
387
+ let x, y
388
+ if (typeof v === 'object' || (isObjectString(v) === true && (v = parseToObject(v)))) {
389
+ if ('x' in v === true) {
390
+ x = v.x
391
+ }
392
+ if ('y' in v === true) {
393
+ y = v.y
394
+ }
395
+ } else {
396
+ v === 'center' || v === 'right' ? (x = v) : (y = v)
397
+ }
398
+
399
+ // Set X position
400
+ if (x === 'center') {
401
+ this.x = '50%'
402
+ this.props['mountX'] = 0.5
403
+ } else if (x === 'right') {
404
+ this.x = '100%'
405
+ this.props['mountX'] = 1
406
+ }
407
+
408
+ // Set Y position
409
+ if (y === 'middle') {
410
+ this.y = '50%'
411
+ this.props['mountY'] = 0.5
412
+ } else if (y === 'bottom') {
413
+ this.y = '100%'
414
+ this.props['mountY'] = 1
415
+ }
416
+ },
386
417
  }
387
418
 
388
419
  const Element = {
@@ -362,30 +362,164 @@ test('Element - Set `fit` property should not set not required keys', (assert) =
362
362
  assert.end()
363
363
  })
364
364
 
365
- test('Element - Set `float` property with value `center`', (assert) => {
365
+ test('Element - Set `placement` property with value `center`', (assert) => {
366
366
  assert.capture(renderer, 'createNode', () => new EventEmitter())
367
367
  const el = createElement()
368
368
 
369
- el.set('float', 'center')
369
+ el.set('placement', 'center')
370
370
 
371
371
  assert.equal(el.node['mountX'], 0.5, 'Node mountX parameter should set to 0.5')
372
372
  assert.equal(el.props.props['mountX'], 0.5, 'Props mountX parameter should set to 0.5')
373
373
  assert.equal(el.node['x'], 960, 'Node x parameter should set to half of parent node width, 960')
374
374
  assert.equal(el.props.props['x'], 960, 'props x parameter should be set to 960')
375
+ assert.equal(
376
+ el.node['y'],
377
+ undefined,
378
+ 'Node Y parameter should not be modified from default value'
379
+ )
375
380
 
376
381
  assert.end()
377
382
  })
378
383
 
379
- test('Element - Set `float` property with value `right`', (assert) => {
384
+ test('Element - Set `placement` property with value `right`', (assert) => {
380
385
  assert.capture(renderer, 'createNode', () => new EventEmitter())
381
386
  const el = createElement()
382
387
 
383
- el.set('float', 'right')
388
+ el.set('placement', 'right')
384
389
 
385
390
  assert.equal(el.node['mountX'], 1, 'Node mountX parameter should set to 1')
386
391
  assert.equal(el.props.props['mountX'], 1, 'Props mountX parameter should set to 1')
387
392
  assert.equal(el.node['x'], 1920, 'Node x parameter should set to parent node full width, 1920')
388
393
  assert.equal(el.props.props['x'], 1920, 'props x parameter should be set to 1920')
394
+ assert.equal(
395
+ el.node['y'],
396
+ undefined,
397
+ 'Node Y parameter should not be modified from default value'
398
+ )
399
+
400
+ assert.end()
401
+ })
402
+
403
+ test('Element - Set `placement` property with value `middle`', (assert) => {
404
+ assert.capture(renderer, 'createNode', () => new EventEmitter())
405
+ const el = createElement()
406
+
407
+ el.set('placement', 'middle')
408
+
409
+ assert.equal(el.node['mountY'], 0.5, 'Node mountY parameter should set to 0.5')
410
+ assert.equal(el.props.props['mountY'], 0.5, 'Props mountY parameter should set to 0.5')
411
+ assert.equal(el.node['y'], 540, 'Node Y parameter should set to half of parent node height, 540')
412
+ assert.equal(el.props.props['y'], 540, 'props Y parameter should be set to 540')
413
+ assert.equal(
414
+ el.node['x'],
415
+ undefined,
416
+ 'Node X parameter should not be modified from default value'
417
+ )
418
+
419
+ assert.end()
420
+ })
421
+
422
+ test('Element - Set `placement` property with value `bottom`', (assert) => {
423
+ assert.capture(renderer, 'createNode', () => new EventEmitter())
424
+ const el = createElement()
425
+
426
+ el.set('placement', 'bottom')
427
+
428
+ assert.equal(el.node['mountY'], 1, 'Node mountY parameter should set to 1')
429
+ assert.equal(el.props.props['mountY'], 1, 'Props mountY parameter should set to 1')
430
+ assert.equal(el.node['y'], 1080, 'Node Y parameter should set to parent node full height, 1080')
431
+ assert.equal(el.props.props['y'], 1080, 'props Y parameter should be set to 1080')
432
+ assert.equal(
433
+ el.node['x'],
434
+ undefined,
435
+ 'Node X parameter should not be modified from default value'
436
+ )
437
+
438
+ assert.end()
439
+ })
440
+
441
+ test('Element - Set `placement` property with value `bottom` & x = 300', (assert) => {
442
+ assert.capture(renderer, 'createNode', () => new EventEmitter())
443
+ const el = createElement()
444
+
445
+ el.set('placement', 'bottom')
446
+
447
+ assert.equal(el.node['mountY'], 1, 'Node mountY parameter should set to 1')
448
+ assert.equal(el.props.props['mountY'], 1, 'Props mountY parameter should set to 1')
449
+ assert.equal(el.node['y'], 1080, 'Node Y parameter should set to parent node full height, 1080')
450
+ assert.equal(el.props.props['y'], 1080, 'props Y parameter should be set to 1080')
451
+
452
+ el.set('x', 300)
453
+
454
+ assert.equal(el.node['x'], 300, 'Node x parameter should set custom value 300')
455
+
456
+ assert.end()
457
+ })
458
+
459
+ test('Element - Set `placement` property with object value `{x:"center", y:"middle"}`', (assert) => {
460
+ assert.capture(renderer, 'createNode', () => new EventEmitter())
461
+ const el = createElement()
462
+
463
+ el.set('placement', "{x:'center', y:'middle'}")
464
+
465
+ assert.equal(el.node['mountX'], 0.5, 'Node mountX parameter should set to 0.5')
466
+ assert.equal(el.props.props['mountX'], 0.5, 'Props mountX parameter should set to 0.5')
467
+ assert.equal(el.node['x'], 960, 'Node x parameter should set to half of parent node width, 960')
468
+
469
+ assert.equal(el.node['mountY'], 0.5, 'Node mountY parameter should set to 0.5')
470
+ assert.equal(el.props.props['mountY'], 0.5, 'Props mountY parameter should set to 0.5')
471
+ assert.equal(el.node['y'], 540, 'Node Y parameter should set to half of parent node height, 540')
472
+
473
+ assert.end()
474
+ })
475
+
476
+ test('Element - Set `placement` property with object value `{x:"center", y:"bottom"}`', (assert) => {
477
+ assert.capture(renderer, 'createNode', () => new EventEmitter())
478
+ const el = createElement()
479
+
480
+ el.set('placement', "{x:'center', y:'bottom'}")
481
+
482
+ assert.equal(el.node['mountX'], 0.5, 'Node mountX parameter should set to 0.5')
483
+ assert.equal(el.props.props['mountX'], 0.5, 'Props mountX parameter should set to 0.5')
484
+ assert.equal(el.node['x'], 960, 'Node x parameter should set to half of parent node width, 960')
485
+
486
+ assert.equal(el.node['mountY'], 1, 'Node mountY parameter should set to 1')
487
+ assert.equal(el.props.props['mountY'], 1, 'Props mountY parameter should set to 1')
488
+ assert.equal(el.node['y'], 1080, 'Node Y parameter should set to parent node full height , 1080')
489
+
490
+ assert.end()
491
+ })
492
+
493
+ test('Element - Set `placement` property with object value `{x:"right", y:"middle"}`', (assert) => {
494
+ assert.capture(renderer, 'createNode', () => new EventEmitter())
495
+ const el = createElement()
496
+
497
+ el.set('placement', "{x:'right', y:'middle'}")
498
+
499
+ assert.equal(el.node['mountX'], 1, 'Node mountX parameter should set to 1')
500
+ assert.equal(el.props.props['mountX'], 1, 'Props mountX parameter should set to 1')
501
+ assert.equal(el.node['x'], 1920, 'Node x parameter should set to parent node full width, 1920')
502
+
503
+ assert.equal(el.node['mountY'], 0.5, 'Node mountY parameter should set to 0.5')
504
+ assert.equal(el.props.props['mountY'], 0.5, 'Props mountY parameter should set to 0.5')
505
+ assert.equal(el.node['y'], 540, 'Node Y parameter should set to half of parent node height, 540')
506
+
507
+ assert.end()
508
+ })
509
+
510
+ test('Element - Set `placement` property with object value `{x:"right", y:"bottom"}`', (assert) => {
511
+ assert.capture(renderer, 'createNode', () => new EventEmitter())
512
+ const el = createElement()
513
+
514
+ el.set('placement', "{x:'right', y:'bottom'}")
515
+
516
+ assert.equal(el.node['mountX'], 1, 'Node mountX parameter should set to 1')
517
+ assert.equal(el.props.props['mountX'], 1, 'Props mountX parameter should set to 1')
518
+ assert.equal(el.node['x'], 1920, 'Node x parameter should set to parent node full width, 1920')
519
+
520
+ assert.equal(el.node['mountY'], 1, 'Node mountY parameter should set to 1')
521
+ assert.equal(el.props.props['mountY'], 1, 'Props mountY parameter should set to 1')
522
+ assert.equal(el.node['y'], 1080, 'Node Y parameter should set to parent node height, 1080')
389
523
 
390
524
  assert.end()
391
525
  })
@@ -24,6 +24,9 @@ export default function (templateObject = { children: [] }) {
24
24
  'let componentType',
25
25
  'const rootComponent = component',
26
26
  'let propData',
27
+ 'let slotComponent',
28
+ 'let inSlot = false',
29
+ 'let slotChildCounter = 0',
27
30
  ],
28
31
  effectsCode: [],
29
32
  context: { props: [], components: this.components },
@@ -79,7 +82,7 @@ const generateElementCode = function (
79
82
  }
80
83
 
81
84
  renderCode.push(`
82
- ${elm} = this.element({parent: parent || 'root'}, component)
85
+ ${elm} = this.element({parent: parent || 'root'}, inSlot === true ? slotComponent : component)
83
86
  `)
84
87
 
85
88
  if (options.forloop) {
@@ -96,7 +99,7 @@ const generateElementCode = function (
96
99
  Object.keys(templateObject).forEach((key) => {
97
100
  if (key === 'slot') {
98
101
  renderCode.push(`
99
- elementConfig${counter}['parent'] = component[Symbol.for('slots')].filter(slot => slot.ref === '${templateObject.slot}').shift() || parent
102
+ elementConfig${counter}['parent'] = component[Symbol.for('slots')] !== undefined && Array.isArray(component[Symbol.for('slots')]) === true && component[Symbol.for('slots')].filter(slot => slot.ref === '${templateObject.slot}').shift() || parent
100
103
  `)
101
104
  }
102
105
 
@@ -149,6 +152,12 @@ const generateElementCode = function (
149
152
 
150
153
  renderCode.push(`${elm}.populate(elementConfig${counter})`)
151
154
 
155
+ renderCode.push(`
156
+ if(inSlot === true) {
157
+ slotChildCounter -= 1
158
+ }
159
+ `)
160
+
152
161
  if (options.forloop) {
153
162
  renderCode.push('}')
154
163
  }
@@ -247,7 +256,8 @@ const generateComponentCode = function (
247
256
 
248
257
  if (${elm}[Symbol.for('slots')][0]) {
249
258
  parent = ${elm}[Symbol.for('slots')][0]
250
- component = ${elm}
259
+ slotComponent = ${elm}
260
+ inSlot = true
251
261
  } else {
252
262
  parent = ${elm}[Symbol.for('children')][0]
253
263
  }
@@ -258,15 +268,24 @@ const generateComponentCode = function (
258
268
  }
259
269
 
260
270
  if (children) {
271
+ if (!options.forloop) {
272
+ renderCode.push(`
273
+ if(inSlot === true) {
274
+ slotChildCounter = ${children.length} + 1
275
+ }
276
+ `)
277
+ }
261
278
  counter++
262
279
  generateElementCode.call(this, { children }, false, { ...options })
263
280
  }
264
- // if (!options.forloop) {
265
- // renderCode.push(`
266
- // // console.log('here', component, rootComponent)
267
- // component = rootComponent
268
- // `)
269
- // }
281
+
282
+ if (!options.forloop) {
283
+ renderCode.push(`
284
+ if (inSlot === true && slotChildCounter === 0) {
285
+ inSlot = false
286
+ }
287
+ `)
288
+ }
270
289
  }
271
290
 
272
291
  const generateForLoopCode = function (templateObject, parent) {
@@ -288,7 +307,7 @@ const generateForLoopCode = function (templateObject, parent) {
288
307
  const result = regex.exec(forLoop)
289
308
 
290
309
  // can be improved with a smarter regex
291
- const [item, index = 'index'] = result[1]
310
+ const [item, index] = result[1]
292
311
  .replace('(', '')
293
312
  .replace(')', '')
294
313
  .split(/\s*,\s*/)
@@ -306,12 +325,15 @@ const generateForLoopCode = function (templateObject, parent) {
306
325
  ctx.renderCode.push(`parent = ${parent}`)
307
326
  }
308
327
 
309
- const indexRegex = new RegExp(`\\$${index}(?!['\\w])`)
310
- const indexResult = indexRegex.exec(key)
311
- if (Array.isArray(indexResult)) {
312
- ctx.renderCode.push(
313
- `console.warn(" Using '${index}' in the key, like key=${key}, is not recommended")`
314
- )
328
+ // If the index variable is not defined, the key attribute would not reference it.
329
+ if (index !== undefined) {
330
+ const indexRegex = new RegExp(`\\$${index}(?!['\\w])`)
331
+ const indexResult = indexRegex.exec(key)
332
+ if (Array.isArray(indexResult)) {
333
+ ctx.renderCode.push(
334
+ `console.warn(" Using '${index}' in the key, like key=${key}, is not recommended")`
335
+ )
336
+ }
315
337
  }
316
338
 
317
339
  const forStartCounter = counter
@@ -324,7 +346,14 @@ const generateForLoopCode = function (templateObject, parent) {
324
346
  let l = rawCollection.length
325
347
  while(l--) {
326
348
  const ${item} = rawCollection[l]
349
+ `)
350
+ // push reference of index variable
351
+ if (index !== undefined) {
352
+ ctx.renderCode.push(`
327
353
  const ${index} = l
354
+ `)
355
+ }
356
+ ctx.renderCode.push(`
328
357
  keys.add('' + ${interpolate(key, '') || 'l'})
329
358
  }
330
359
  `)
@@ -341,8 +370,15 @@ const generateForLoopCode = function (templateObject, parent) {
341
370
  for(let __index = 0; __index < length; __index++) {
342
371
  const scope = Object.create(component)
343
372
  parent = ${parent}
344
- scope['${index}'] = __index
345
373
  scope['${item}'] = rawCollection[__index]
374
+ `)
375
+ // If the index variable is declared, include it in the scope object
376
+ if (index !== '') {
377
+ ctx.renderCode.push(`
378
+ scope['${index}'] = __index
379
+ `)
380
+ }
381
+ ctx.renderCode.push(`
346
382
  scope['key'] = '' + ${forKey || '__index'}
347
383
  `)
348
384
  if ('ref' in templateObject && templateObject.ref.indexOf('$') === -1) {
@@ -15,7 +15,6 @@
15
15
  * SPDX-License-Identifier: Apache-2.0
16
16
  */
17
17
 
18
- import { ImageTexture, Texture } from '@lightningjs/renderer'
19
18
  import { track, trigger, pauseTracking, resumeTracking } from './effect.js'
20
19
  import symbols from '../symbols.js'
21
20
  import deepEqualArray from '../../helpers/deepEqualArray.js'
@@ -30,14 +29,15 @@ export const getRaw = (value) => {
30
29
  }
31
30
 
32
31
  const reactiveProxy = (original, _parent = null, _key, global) => {
33
- // don't create a proxy when a Blits component or an Image Texture
34
- // is assigned to a state variable
35
- if (typeof original === 'object') {
36
- if (original[symbols.id] !== undefined) return original
37
- if (original.constructor.name === ImageTexture.name) return original
38
- if (original.txManager !== undefined) return original
32
+ // only create a proxy for plain objects. Other classes, Blits components, renderer textures etc
33
+ // are returned unmodified and will not become reactive
34
+ if (
35
+ typeof original === 'object' &&
36
+ Array.isArray(original) === false &&
37
+ Object.getPrototypeOf(original) !== Object.prototype
38
+ ) {
39
+ return original
39
40
  }
40
-
41
41
  // if original object is already a proxy, don't create a new one but return the existing one instead
42
42
  const existingProxy = proxyMap.get(original)
43
43
  if (existingProxy !== undefined) {
@@ -16,23 +16,52 @@
16
16
  */
17
17
 
18
18
  import blitsfileconverter from '../src/lib/blitsfileconverter/blitsfileconverter.js'
19
+ import path from 'node:path'
20
+ import fs from 'node:fs'
21
+ import { createRequire } from 'module'
19
22
 
20
23
  export default function blitsFileType() {
21
24
  return {
22
25
  name: 'vite-plugin-blits-file-type',
23
26
  enforce: 'pre',
24
- transform(src, id) {
27
+
28
+ resolveId(source, importer) {
29
+ if (source.endsWith('.blits')) {
30
+ return importer ? path.resolve(path.dirname(importer), source) : path.resolve(source)
31
+ }
32
+ return null
33
+ },
34
+
35
+ load(id) {
25
36
  if (id.endsWith('.blits')) {
26
- try {
27
- const transformedCode = blitsfileconverter(src)
28
- return {
29
- code: transformedCode,
30
- map: null, // no source map
37
+ const source = fs.readFileSync(id, 'utf-8')
38
+ let code = blitsfileconverter(source)
39
+
40
+ // Check for TypeScript and transpile to JS if needed
41
+ if (/<script\s+lang=["']ts["']/.test(source)) {
42
+ // Resolve the local typescript dependency
43
+ const userRequire = createRequire(process.cwd() + '/')
44
+ let ts
45
+ try {
46
+ ts = userRequire('typescript')
47
+ } catch (err) {
48
+ throw new Error(
49
+ `\n\nThe file "${id}" contains \`lang="ts"\`, indicating it uses TypeScript. \nTo enable TypeScript support, please install the 'typescript' package as a dev dependency by running:\n\n` +
50
+ ' npm install --save-dev typescript\n\n'
51
+ )
31
52
  }
32
- } catch (error) {
33
- this.error(error)
53
+ const transpiled = ts.transpileModule(code, {
54
+ compilerOptions: { target: ts.ScriptTarget.ESNext, module: ts.ModuleKind.ESNext },
55
+ })
56
+ code = transpiled.outputText
57
+ }
58
+
59
+ return {
60
+ code,
61
+ map: null,
34
62
  }
35
63
  }
64
+ return null
36
65
  },
37
66
  }
38
67
  }
@@ -30,12 +30,13 @@ export default function () {
30
30
 
31
31
  const fileExtension = path.extname(filePath)
32
32
 
33
- // we should only precompile .js and .ts files
34
- if (fileExtension === '.js' || fileExtension === '.ts') {
33
+ // we should only precompile .blits, .js and .ts files
34
+ if (fileExtension === '.js' || fileExtension === '.ts' || fileExtension === '.blits') {
35
35
  return compiler(source, filePath)
36
36
  }
37
- const relativePath = path.relative(process.cwd(), filePath)
38
- return compiler(source, relativePath)
37
+
38
+ // vite expects null if there is no modification
39
+ return null
39
40
  },
40
41
  }
41
42
  }