@lightningjs/blits 1.14.1 → 1.15.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## v1.15.0
4
+
5
+ _14 jan 2025_
6
+
7
+ - Added `placement`-attribute for easily aligning Elements to predefined locations (i.e. `center`, `right`, `middle`, `bottom`)
8
+ - Removed automatic injection of `index` variable into forloop-scope to prevent unexpected naming collisions
9
+
10
+
3
11
  ## v1.14.1
4
12
 
5
13
  _8 jan 2025_
@@ -36,6 +36,38 @@ Besides using values in pixels (i.e. `w="100" h="300"`), you can also specify _p
36
36
  The percentage value specified for `w` and `x` will be calculated as the percentage of the _width_ (`w`) of the parent element.
37
37
  And the percentage specified for `h` and `y` will use the _height_ (`h`) of the parent element as the base of the percentage calculation.
38
38
 
39
+ ### Predefined placement options
40
+
41
+ In addition to the absolute positions that Blits and Lightning use, there are a few predefined placement options available. By adding the `placement`-attribute to an Element we can easily align it to the _center_, _left_, _right_, _top_, _bottom_, or even a combination like `{y: 'middle', x: 'left'}`, without having to calculate the positions yourself.
42
+
43
+
44
+ On the `x`-axis the following _placement_ options can be used: `left` (default), `center` and `right`. On the `y`-axis the _placement_ attribute accepts `top` (default), `middle` and `bottom`.
45
+
46
+ The _placement_ attribute also accepts and object with an `x` and a `y` key, to specify a placement for both axes.
47
+
48
+ The placement of an Element is calculated based on the dimensions of it's direct parent. This means that the containing Element _must_ have it's own dimensions (i.e. a `w` and a `h` attribute).
49
+
50
+ ```xml
51
+ <Element w="300" h="300">
52
+ <!-- x placement -->
53
+ <Element w="40" h="40" placement="left" color="#818cf8" />
54
+ <Element w="40" h="40" placement="center" color="#2563eb" />
55
+ <Element w="40" h="40" placement="right" color="#1e40af" />
56
+
57
+ <!-- y placement -->
58
+ <Element w="40" h="40" x="54" placement="top" color="#f472b6" />
59
+ <Element w="40" h="40" x="54" placement="middle" color="#db2777" />
60
+ <Element w="40" h="40" x="54" placement="bottom" color="#be185d" />
61
+
62
+ <!-- x/y placement -->
63
+ <Element w="40" h="40" placement="{x: 'center', y: 'bottom'}" color="#a3e635" />
64
+ <Element w="40" h="40" placement="{x: 'right', y: 'middle'}" color="#65a30d" />
65
+ <Element w="40" h="40" placement="{x: 'center', y: 'middle'}" color="#4d7c0f" />
66
+ </Element>
67
+ ```
68
+
69
+ > note: the `x` or `y` attributes on the Element are ignored for those axes defined in the `placement`-attribute
70
+
39
71
  ## Colors
40
72
 
41
73
  By default, Elements have a transparent background color. The `color` attribute can be used to give an Element a color.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightningjs/blits",
3
- "version": "1.14.1",
3
+ "version": "1.15.0",
4
4
  "description": "Blits: The Lightning 3 App Development Framework",
5
5
  "bin": "bin/index.js",
6
6
  "exports": {
@@ -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
  })
@@ -288,7 +288,7 @@ const generateForLoopCode = function (templateObject, parent) {
288
288
  const result = regex.exec(forLoop)
289
289
 
290
290
  // can be improved with a smarter regex
291
- const [item, index = 'index'] = result[1]
291
+ const [item, index] = result[1]
292
292
  .replace('(', '')
293
293
  .replace(')', '')
294
294
  .split(/\s*,\s*/)
@@ -306,12 +306,15 @@ const generateForLoopCode = function (templateObject, parent) {
306
306
  ctx.renderCode.push(`parent = ${parent}`)
307
307
  }
308
308
 
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
- )
309
+ // If the index variable is not defined, the key attribute would not reference it.
310
+ if (index !== undefined) {
311
+ const indexRegex = new RegExp(`\\$${index}(?!['\\w])`)
312
+ const indexResult = indexRegex.exec(key)
313
+ if (Array.isArray(indexResult)) {
314
+ ctx.renderCode.push(
315
+ `console.warn(" Using '${index}' in the key, like key=${key}, is not recommended")`
316
+ )
317
+ }
315
318
  }
316
319
 
317
320
  const forStartCounter = counter
@@ -324,7 +327,14 @@ const generateForLoopCode = function (templateObject, parent) {
324
327
  let l = rawCollection.length
325
328
  while(l--) {
326
329
  const ${item} = rawCollection[l]
330
+ `)
331
+ // push reference of index variable
332
+ if (index !== undefined) {
333
+ ctx.renderCode.push(`
327
334
  const ${index} = l
335
+ `)
336
+ }
337
+ ctx.renderCode.push(`
328
338
  keys.add('' + ${interpolate(key, '') || 'l'})
329
339
  }
330
340
  `)
@@ -341,8 +351,15 @@ const generateForLoopCode = function (templateObject, parent) {
341
351
  for(let __index = 0; __index < length; __index++) {
342
352
  const scope = Object.create(component)
343
353
  parent = ${parent}
344
- scope['${index}'] = __index
345
354
  scope['${item}'] = rawCollection[__index]
355
+ `)
356
+ // If the index variable is declared, include it in the scope object
357
+ if (index !== '') {
358
+ ctx.renderCode.push(`
359
+ scope['${index}'] = __index
360
+ `)
361
+ }
362
+ ctx.renderCode.push(`
346
363
  scope['key'] = '' + ${forKey || '__index'}
347
364
  `)
348
365
  if ('ref' in templateObject && templateObject.ref.indexOf('$') === -1) {