@lightningjs/blits 1.11.0 → 1.13.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.
@@ -13,13 +13,13 @@ jobs:
13
13
  - name: Trigger Deployment
14
14
  timeout-minutes: 5
15
15
  run: |
16
- response=$(curl -X POST -w "%{http_code}" -s -o /dev/null \
16
+ response=$(curl -X POST -d {} -H "Content-Type:application/json" -w "%{http_code}" -s -o /dev/null \
17
17
  --max-time 30 \
18
18
  --connect-timeout 10 \
19
19
  --retry 2 \
20
20
  --retry-delay 5 \
21
- ${{ secrets.WEBSITE_DEPLOY_URL }})
22
- if [ "$response" -eq 200 ]; then
21
+ "${{ secrets.WEBSITE_DEPLOY_URL }}")
22
+ if [ "$response" -ge 200 ] && [ "$response" -lt 300 ]; then
23
23
  echo "Deployment triggered successfully"
24
24
  else
25
25
  echo "Failed to trigger deployment. Status code: $response"
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## v1.13.0
4
+
5
+ _5 dec 2024_
6
+
7
+ - Added `padding` attribute to Layout component
8
+ - Added logging of Blits and Renderer version used in App
9
+ - Fixed show attribute for elements without predefined width and height
10
+ - Added automatic proxy fallback (when no browser support) for reactivity
11
+ - Fixed and added tests related to Blits Element
12
+ - Fixed memory issue with effects used in for-loop
13
+ - Fixed memory issue when changing props in dynamic shader effects
14
+ - Updated renderer to v2.8.0
15
+
16
+ ## v1.12.0
17
+
18
+ _26 nov 2024_
19
+
20
+ - Fixed `$hasFocus` state variable not being set when navigating back to a page with `keepAlive` enabled
21
+ - Added `@updated`-event to Layout component
22
+ - Fixed issue with simple config for Theme plugin
23
+
24
+
3
25
  ## v1.11.0
4
26
 
5
27
  _19 nov 2024_
package/blits.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ /// <reference types="./index.d.ts" />
2
+
3
+ declare module '*.blits' {
4
+ const component: any;
5
+ export default component;
6
+ }
@@ -61,9 +61,61 @@ It's also possible to nest layouts. Simply place a new `<Layout>`-tag, with it's
61
61
 
62
62
  And of course you can nest _vertical_ Layouts inside a _horizontal_ one - and vice versa.
63
63
 
64
+ ### Padding
65
+
66
+ By default a `<Layout />`-tag will be resized to the exact dimensions as the content it is containing. The `padding`-attribute can be used to add spacing between the content and the edges of the Layout Component.
67
+
68
+ The `padding`-attribute accepts a `number` or an `object`. When passed a number, that padding will be applied equally to all sides. With an object value, the padding can be controlled for each side individually.
69
+
70
+ Valid keys in the _padding-object_ are: `top`, `bottom`, `left`, `right`, `x` and `y`. The `x` and `y` keys can be used to define the same values for `top` and `bottom`, and `left` and `right` in one go. When a value is not specified for a side, it will default to `0`.
71
+
72
+ ```xml
73
+ <Layout color="silver" padding="10" >
74
+ <Element w="40" h="40" color="red" />
75
+ <Element w="80" h="40" color="blue" />
76
+ <Element w="40" h="40" color="green" />
77
+ </Layout>
78
+ ```
79
+
80
+ ```xml
81
+ <Layout color="silver" padding="{x: 20, top: 30, bottom: 10}">
82
+ <Element w="40" h="40" color="red" />
83
+ <Element w="80" h="40" color="blue" />
84
+ <Element w="40" h="40" color="green" />
85
+ </Layout>
86
+ ```
87
+
64
88
  ### Transitioning layouts
65
89
 
66
- The `<Layout>`-component can also take into account when dimensions of children change with a transition applied to it. The component will recalculate the position of its children as the transition progresses, making sure that elements are always perfectly positioned relative to one another.
90
+ The `<Layout>`-component can also take into account when dimensions of children change with a transition applied to it (i.e. `<Element :w.transition="$myWidth">`). The component will recalculate the position of its children as the transition progresses, making sure that elements are always perfectly positioned relative to one another.
91
+
92
+ ### Updated event
93
+
94
+ The `<Layout>`-tag automatically updates its dimensions based on the dimensions of its children. After each update in the children, the an `updated`-event is emitted on the `<Layout>`-tag. It will receive the current dimensions of the layout.
95
+
96
+ You can tap into this event by adding an `@updated`-attribute to the `<Layout />`-tag and refer to a method in your Component logic.
97
+
98
+
99
+ ```js
100
+ export default Blits.Component('LayoutUpdate', {
101
+ template: `
102
+ <Element>
103
+ <Layout @updated="$layoutUpdate">
104
+ <Element :w="$width" h="40" color="red" />
105
+ <Element :w="$width" h="40" color="blue" />
106
+ <Element :w="$width" h="40" color="green" />
107
+ </Layout>
108
+ </Element>
109
+ `,
110
+ methods: {
111
+ layoutUpdate(dimensions, el) {
112
+ console.log(`Layout (${el.nodeId}) dimensions updated! Width: ${dimensions.w}, Height: ${dimensions.h}`)
113
+ }
114
+ }
115
+ })
116
+ ```
117
+
118
+ > Please be aware that the `@updated` event can fire multiple times. The size of the Layout-tag is recalculated for each change in children. Also note that the `@updated` event does not guarantee a change in dimensions. It is possible that it fires for a children update, but remains the same size.
67
119
 
68
120
  ## Performance
69
121
 
@@ -75,3 +75,24 @@ The second `$`-sign is needed, since the appState plugin itself is prefixed with
75
75
 
76
76
  In order to read or update variables on the global app state inside the component logic, you can access the entire state via `this.$appState`.
77
77
 
78
+ ### Watching global state
79
+
80
+ Similar to component state, you can watch for changes in the global state as well. You can to refer to global state variables using _dot-notation_ in the watcher function name, and _prefixing_ it with `$appState`. This means that you need to define your watcher as a `String`, matching one of the examples below, depending on your style preference.
81
+
82
+ ```js
83
+ Blits.Component('MyComponent', {
84
+ template: `
85
+ <Element>
86
+ <Text :content="$$appState.loggedIn ? $$appState.user.name : 'Not logged in'" />
87
+ </Element>
88
+ `,
89
+ watch: {
90
+ '$appState.fooBar'(v, old) {
91
+ console.log(`$appState.fooBar changed from ${old} to ${v}`)
92
+ },
93
+ '$appState.my.nested.data': function(v, old) {
94
+ console.log(`My nested App state data changed!`)
95
+ }
96
+ },
97
+ })
98
+ ```
package/index.d.ts CHANGED
@@ -15,6 +15,9 @@
15
15
  * SPDX-License-Identifier: Apache-2.0
16
16
  */
17
17
 
18
+ // blits file type reference
19
+ /// <reference path="./blits.d.ts" />
20
+
18
21
  import {type ShaderEffect as RendererShaderEffect, type WebGlCoreShader} from '@lightningjs/renderer'
19
22
 
20
23
  declare module '@lightningjs/blits' {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightningjs/blits",
3
- "version": "1.11.0",
3
+ "version": "1.13.0",
4
4
  "description": "Blits: The Lightning 3 App Development Framework",
5
5
  "bin": "bin/index.js",
6
6
  "exports": {
@@ -50,7 +50,7 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "@lightningjs/msdf-generator": "^1.1.0",
53
- "@lightningjs/renderer": "^2.7.1"
53
+ "@lightningjs/renderer": "^2.8.0"
54
54
  },
55
55
  "repository": {
56
56
  "type": "git",
@@ -64,6 +64,14 @@ export default {
64
64
  enumerable: true,
65
65
  configurable: false,
66
66
  },
67
+ [symbols.removeGlobalEffects]: {
68
+ value: function (effects = []) {
69
+ removeGlobalEffects(effects)
70
+ },
71
+ writable: false,
72
+ enumerable: false,
73
+ configurable: false,
74
+ },
67
75
  select: {
68
76
  value: function (ref) {
69
77
  Log.warn('this.select is deprecated, use this.$select instead')
@@ -22,25 +22,70 @@ import { Log } from '../../lib/log.js'
22
22
  import symbols from '../../lib/symbols.js'
23
23
  import Settings from '../../settings.js'
24
24
 
25
+ const createPaddingObject = (padding, direction) => {
26
+ if (padding === undefined) {
27
+ return { start: 0, end: 0, oppositeStart: 0, oppositeEnd: 0 }
28
+ }
29
+
30
+ if (typeof padding === 'number') {
31
+ return { start: padding, end: padding, oppositeStart: padding, oppositeEnd: padding }
32
+ }
33
+
34
+ // todo: do we need to do this runtime every time? or can we optimize this?
35
+ if (isObjectString(padding) === true) {
36
+ padding = parseToObject(padding)
37
+ }
38
+
39
+ if (typeof padding === 'object') {
40
+ const {
41
+ top = undefined,
42
+ right = undefined,
43
+ bottom = undefined,
44
+ left = undefined,
45
+ x = 0,
46
+ y = 0,
47
+ } = padding
48
+
49
+ // use specific values if provided, otherwise fall back to x or y
50
+ return direction === 'vertical'
51
+ ? {
52
+ start: top !== undefined ? top : y,
53
+ end: bottom !== undefined ? bottom : y,
54
+ oppositeStart: left !== undefined ? left : x,
55
+ oppositeEnd: right !== undefined ? right : x,
56
+ }
57
+ : {
58
+ start: left !== undefined ? left : x,
59
+ end: right !== undefined ? right : x,
60
+ oppositeStart: top !== undefined ? top : y,
61
+ oppositeEnd: bottom !== undefined ? bottom : y,
62
+ }
63
+ }
64
+ return { start: 0, end: 0, oppositeStart: 0, oppositeEnd: 0 }
65
+ }
66
+
25
67
  const layoutFn = function (config) {
26
- let offset = 0
27
68
  const position = config.direction === 'vertical' ? 'y' : 'x'
28
69
  const oppositePosition = config.direction === 'vertical' ? 'x' : 'y'
29
70
  const oppositeMount = config.direction === 'vertical' ? 'mountX' : 'mountY'
30
71
  const dimension = config.direction === 'vertical' ? 'height' : 'width'
31
72
  const oppositeDimension = config.direction === 'vertical' ? 'width' : 'height'
73
+ const padding = createPaddingObject(config.padding, config.direction)
74
+
75
+ let offset = padding.start
32
76
 
33
- const children = this.children
77
+ const children = this.node.children
34
78
  const childrenLength = children.length
35
79
  let otherDimension = 0
36
80
  const gap = config.gap || 0
37
81
  for (let i = 0; i < childrenLength; i++) {
38
82
  const node = children[i]
39
83
  node[position] = offset
84
+ node[oppositePosition] = padding.oppositeStart
40
85
  // todo: temporary text check, due to 1px width of empty text node
41
86
  if (dimension === 'width') {
42
87
  offset += node.width + (node.width !== ('text' in node ? 1 : 0) ? gap : 0)
43
- } else if (dimension === 'height') {
88
+ } else {
44
89
  offset +=
45
90
  'text' in node
46
91
  ? node.width > 1
@@ -50,11 +95,14 @@ const layoutFn = function (config) {
50
95
  ? node.height + gap
51
96
  : 0
52
97
  }
53
- otherDimension = Math.max(otherDimension, node[oppositeDimension])
98
+ otherDimension = Math.max(
99
+ otherDimension,
100
+ node[oppositeDimension] + padding.oppositeStart + padding.oppositeEnd
101
+ )
54
102
  }
55
103
  // adjust the size of the layout container
56
- this[dimension] = offset - gap
57
- this[oppositeDimension] = otherDimension
104
+ this.node[dimension] = offset - gap + padding.end
105
+ this.node[oppositeDimension] = otherDimension
58
106
 
59
107
  const align = {
60
108
  start: 0,
@@ -69,6 +117,11 @@ const layoutFn = function (config) {
69
117
  node[oppositeMount] = align
70
118
  }
71
119
  }
120
+
121
+ // emit an updated event
122
+ if (config['@updated'] !== undefined) {
123
+ config['@updated']({ w: this.node.width, h: this.node.height }, this)
124
+ }
72
125
  }
73
126
 
74
127
  const isTransition = (value) => {
@@ -243,8 +296,8 @@ const propsTransformer = {
243
296
  set show(v) {
244
297
  if (v) {
245
298
  this.props['alpha'] = 1
246
- this.props['width'] = this.raw['w'] || this.raw['width']
247
- this.props['height'] = this.raw['h'] || this.raw['height']
299
+ this.props['width'] = this.raw['w'] || this.raw['width'] || 0
300
+ this.props['height'] = this.raw['h'] || this.raw['height'] || 0
248
301
  } else {
249
302
  this.props['alpha'] = 0
250
303
  this.props['width'] = 0
@@ -267,9 +320,23 @@ const propsTransformer = {
267
320
  v[i].props.color = colors.normalize(v[i].props.color)
268
321
  }
269
322
  }
270
- this.props['shader'] = renderer.createShader('DynamicShader', {
271
- effects: v,
272
- })
323
+
324
+ if (this.element.node === undefined) {
325
+ this.props['shader'] = renderer.createShader('DynamicShader', {
326
+ effects: v.map((effect) => {
327
+ return renderer.createEffect(effect.type, effect.props, effect.type)
328
+ }),
329
+ })
330
+ } else {
331
+ for (let i = 0; i < v.length; i++) {
332
+ const target = this.element.node.shader.props[v[i].type]
333
+ const props = Object.keys(v[i].props)
334
+
335
+ for (let j = 0; j < props.length; j++) {
336
+ target[props[j]] = v[i].props[props[j]]
337
+ }
338
+ }
339
+ }
273
340
  },
274
341
  set clipping(v) {
275
342
  this.props['clipping'] = v
@@ -363,7 +430,7 @@ const Element = {
363
430
  }
364
431
 
365
432
  if (props.__layout === true) {
366
- this.triggerLayout = layoutFn.bind(this.node)
433
+ this.triggerLayout = layoutFn.bind(this)
367
434
  }
368
435
 
369
436
  if (this.config.parent.props !== undefined && this.config.parent.props.__layout === true) {
@@ -24,6 +24,8 @@ import symbols from '../../lib/symbols.js'
24
24
 
25
25
  initLog()
26
26
 
27
+ let elementRef
28
+
27
29
  test('Type', (assert) => {
28
30
  assert.ok(element instanceof Function, 'Element should be a factory function')
29
31
  assert.end()
@@ -105,10 +107,98 @@ test('Element - Set `w` property', (assert) => {
105
107
  assert.end()
106
108
  })
107
109
 
110
+ test('Element- Set `w` property in percentage', (assert) => {
111
+ assert.capture(renderer, 'createNode', () => new EventEmitter())
112
+ const el = createElement()
113
+
114
+ el.set('w', '50%')
115
+
116
+ assert.equal(el.node['width'], 960, 'Node width parameter should be set to 960')
117
+ assert.equal(el.props.props['width'], 960, 'Props width parameter should be set')
118
+ assert.equal(el.props.raw['w'], '50%', "Props' raw map entry should be added")
119
+
120
+ assert.end()
121
+ })
122
+
123
+ test('Element - Set `h` property', (assert) => {
124
+ assert.capture(renderer, 'createNode', () => new EventEmitter())
125
+ const el = createElement()
126
+
127
+ el.set('h', 100)
128
+
129
+ assert.equal(el.node['height'], 100, 'Node height parameter should be set')
130
+ assert.equal(el.props.props['height'], 100, 'Props height parameter should be set')
131
+ assert.equal(el.props.raw['h'], 100, "Props' raw map entry should be added")
132
+ assert.end()
133
+ })
134
+
135
+ test('Element- Set `h` property in percentage', (assert) => {
136
+ assert.capture(renderer, 'createNode', () => new EventEmitter())
137
+ const el = createElement()
138
+
139
+ el.set('h', '50%')
140
+
141
+ assert.equal(el.node['height'], 540, 'Node width parameter should be set to 540')
142
+ assert.equal(el.props.props['height'], 540, 'Props width parameter should be set')
143
+ assert.equal(el.props.raw['h'], '50%', "Props' raw map entry should be added")
144
+
145
+ assert.end()
146
+ })
147
+
148
+ test('Element - Set `x` property', (assert) => {
149
+ assert.capture(renderer, 'createNode', () => new EventEmitter())
150
+ const el = createElement()
151
+
152
+ el.set('x', 100)
153
+
154
+ assert.equal(el.node['x'], 100, 'Node x parameter should be set')
155
+ assert.equal(el.props.props['x'], 100, 'Props x parameter should be set')
156
+ assert.equal(el.props.raw['x'], 100, "Props' raw map entry should be added")
157
+ assert.end()
158
+ })
159
+
160
+ test('Element- Set `x` property in percentage', (assert) => {
161
+ assert.capture(renderer, 'createNode', () => new EventEmitter())
162
+ const el = createElement()
163
+
164
+ el.set('x', '50%')
165
+
166
+ assert.equal(el.node['x'], 960, 'Node x parameter should be set to 960')
167
+ assert.equal(el.props.props['x'], 960, 'Props x parameter should be set')
168
+ assert.equal(el.props.raw['x'], '50%', "Props' raw map entry should be added")
169
+
170
+ assert.end()
171
+ })
172
+
173
+ test('Element - Set `y` property', (assert) => {
174
+ assert.capture(renderer, 'createNode', () => new EventEmitter())
175
+ const el = createElement()
176
+
177
+ el.set('y', 100)
178
+
179
+ assert.equal(el.node['y'], 100, 'Node y parameter should be set')
180
+ assert.equal(el.props.props['y'], 100, 'Props y parameter should be set')
181
+ assert.equal(el.props.raw['y'], 100, "Props' raw map entry should be added")
182
+ assert.end()
183
+ })
184
+
185
+ test('Element- Set `y` property in percentage', (assert) => {
186
+ assert.capture(renderer, 'createNode', () => new EventEmitter())
187
+ const el = createElement()
188
+
189
+ el.set('y', '50%')
190
+
191
+ assert.equal(el.node['y'], 540, 'Node y parameter should be set to 540')
192
+ assert.equal(el.props.props['y'], 540, 'Props y parameter should be set')
193
+ assert.equal(el.props.raw['y'], '50%', "Props' raw map entry should be added")
194
+
195
+ assert.end()
196
+ })
197
+
108
198
  test('Element - Set `mount` property', (assert) => {
109
199
  assert.capture(renderer, 'createNode', () => new EventEmitter())
110
200
  const el = createElement()
111
- const value = { x: 10, y: 20 }
201
+ const value = '{ x: 10, y: 20 }'
112
202
 
113
203
  el.set('mount', value)
114
204
 
@@ -271,8 +361,87 @@ test('Element - Set `fit` property should not set not required keys', (assert) =
271
361
  )
272
362
  assert.end()
273
363
  })
364
+
365
+ test('Element - Set `float` property with value `center`', (assert) => {
366
+ assert.capture(renderer, 'createNode', () => new EventEmitter())
367
+ const el = createElement()
368
+
369
+ el.set('float', 'center')
370
+
371
+ assert.equal(el.node['mountX'], 0.5, 'Node mountX parameter should set to 0.5')
372
+ assert.equal(el.props.props['mountX'], 0.5, 'Props mountX parameter should set to 0.5')
373
+ assert.equal(el.node['x'], 960, 'Node x parameter should set to half of parent node width, 960')
374
+ assert.equal(el.props.props['x'], 960, 'props x parameter should be set to 960')
375
+
376
+ assert.end()
377
+ })
378
+
379
+ test('Element - Set `float` property with value `right`', (assert) => {
380
+ assert.capture(renderer, 'createNode', () => new EventEmitter())
381
+ const el = createElement()
382
+
383
+ el.set('float', 'right')
384
+
385
+ assert.equal(el.node['mountX'], 1, 'Node mountX parameter should set to 1')
386
+ assert.equal(el.props.props['mountX'], 1, 'Props mountX parameter should set to 1')
387
+ assert.equal(el.node['x'], 1920, 'Node x parameter should set to parent node full width, 1920')
388
+ assert.equal(el.props.props['x'], 1920, 'props x parameter should be set to 1920')
389
+
390
+ assert.end()
391
+ })
392
+
393
+ test('Element - Set `float` property with value `unknown`', (assert) => {
394
+ assert.capture(renderer, 'createNode', () => new EventEmitter())
395
+ const el = createElement()
396
+
397
+ el.set('float', 'unknown')
398
+
399
+ assert.equal(el.node['mountX'], undefined, 'Node mountX parameter should not be changed')
400
+ assert.equal(el.props.props['mountX'], undefined, 'Props mountX parameter should not be changed')
401
+ assert.equal(el.node['x'], undefined, 'Node x parameter should not get updated')
402
+ assert.equal(el.props.props['x'], undefined, 'props x parameter should not be changed')
403
+
404
+ assert.end()
405
+ })
406
+
407
+ test('Element - Set `show` property as false', (assert) => {
408
+ assert.capture(renderer, 'createNode', () => new EventEmitter())
409
+ const el = createElement()
410
+
411
+ el.set('w', 960)
412
+ el.set('h', 540)
413
+
414
+ el.set('show', false)
415
+
416
+ elementRef = el
417
+
418
+ assert.equal(el.node['alpha'], 0, 'Node alpha parameter should set to 0')
419
+ assert.equal(el.props.props['alpha'], 0, 'Props alpha parameter should set')
420
+ assert.equal(el.node['width'], 0, 'Node width parameter should set to 0')
421
+ assert.equal(el.props.props['width'], 0, 'props width parameter should set to 0')
422
+ assert.equal(el.node['height'], 0, 'Node height parameter should set to 0')
423
+ assert.equal(el.props.props['height'], 0, 'props height parameter should set to 0')
424
+
425
+ assert.end()
426
+ })
427
+
428
+ test('Element - Set `show` property as false', (assert) => {
429
+ const el = elementRef
430
+
431
+ el.set('show', true)
432
+
433
+ assert.equal(el.node['alpha'], 1, 'Node alpha parameter should be set')
434
+ assert.equal(el.props.props['alpha'], 1, 'Props alpha parameter should be set')
435
+ assert.equal(el.node['width'], 960, 'Node width parameter should be set')
436
+ assert.equal(el.props.props['width'], 960, 'props width parameter should be set')
437
+ assert.equal(el.node['height'], 540, 'Node height parameter should be set')
438
+ assert.equal(el.props.props['height'], 540, 'props height parameter should be set')
439
+
440
+ assert.end()
441
+ })
442
+
274
443
  function createElement() {
275
- const el = element({}, {})
444
+ const el = element({ parent: { node: { width: 1920, height: 1080 } } }, {})
276
445
  el.populate({
277
446
  parent: {
278
447
  node: new EventEmitter(),
package/src/launch.js CHANGED
@@ -16,8 +16,9 @@
16
16
  */
17
17
 
18
18
  import Settings from './settings.js'
19
- import { initLog } from './lib/log.js'
19
+ import { initLog, Log } from './lib/log.js'
20
20
  import engine from './engine.js'
21
+ import packageInfo from '../package.json'
21
22
 
22
23
  export let renderer = {}
23
24
  export const stage = {}
@@ -26,6 +27,8 @@ export default (App, target, settings) => {
26
27
  Settings.set(settings)
27
28
 
28
29
  initLog()
30
+ Log.info('Blits Version ', packageInfo.version)
31
+ Log.info('Renderer Version ', packageInfo.dependencies['@lightningjs/renderer'])
29
32
 
30
33
  stage.element = engine.Element
31
34
 
@@ -337,6 +337,7 @@ const generateForLoopCode = function (templateObject, parent) {
337
337
  ctx.renderCode.push(`
338
338
  created.length = 0
339
339
  const length = rawCollection.length
340
+ const effects = []
340
341
  for(let __index = 0; __index < length; __index++) {
341
342
  const scope = Object.create(component)
342
343
  parent = ${parent}
@@ -405,7 +406,7 @@ const generateForLoopCode = function (templateObject, parent) {
405
406
  ${effect}
406
407
  }
407
408
  effect(eff${index}, ${key})
408
- component[Symbol.for('effects')].push(eff${index})
409
+ effects.push(eff${index})
409
410
  `)
410
411
  } else {
411
412
  // props shouldn't be wrapped in an effect, but simply passed on
@@ -416,6 +417,7 @@ const generateForLoopCode = function (templateObject, parent) {
416
417
  })
417
418
  ctx.renderCode.push(`
418
419
  }
420
+ return effects
419
421
  }`)
420
422
 
421
423
  // generate code that destroys items
@@ -436,7 +438,7 @@ const generateForLoopCode = function (templateObject, parent) {
436
438
  `)
437
439
  }
438
440
  destroyCode.push(`
439
- }
441
+ }
440
442
  }
441
443
  `)
442
444
 
@@ -451,8 +453,14 @@ const generateForLoopCode = function (templateObject, parent) {
451
453
  }
452
454
 
453
455
  ctx.renderCode.push(`
456
+ let forEffects${forStartCounter}
454
457
  effect(() => {
455
- forloop${forStartCounter}(${cast(result[2], ':for')}, elms, created${forStartCounter})
458
+ component[Symbol.for('removeGlobalEffects')](forEffects${forStartCounter})
459
+ forEffects${forStartCounter} = null
460
+ forEffects${forStartCounter} = forloop${forStartCounter}(${cast(
461
+ result[2],
462
+ ':for'
463
+ )}, elms, created${forStartCounter})
456
464
  }, '${effectKey}' )
457
465
  `)
458
466
 
@@ -164,7 +164,13 @@ const reactiveDefineProperty = (target, global) => {
164
164
  return target
165
165
  }
166
166
 
167
+ const hasProxySupport = typeof Proxy !== 'undefined'
168
+
167
169
  export const reactive = (target, mode = 'Proxy', global = false) => {
170
+ // force defineProperty reactivity mode when no proxy support
171
+ if (hasProxySupport === false) {
172
+ mode = 'defineProperty'
173
+ }
168
174
  return mode === 'defineProperty'
169
175
  ? reactiveDefineProperty(target, global)
170
176
  : reactiveProxy(target, undefined, undefined, global)
@@ -48,4 +48,6 @@ export default {
48
48
  isComponent: Symbol.for('isComponent'),
49
49
  // Symbol 'effects' utilized within generated code
50
50
  effects: Symbol.for('effects'),
51
+ // Symbol 'removeGlobalEffects' utilized within generated code
52
+ removeGlobalEffects: Symbol.for('removeGlobalEffects'),
51
53
  }
@@ -72,7 +72,7 @@ export default {
72
72
  // theme get function when current theme is modified
73
73
  state.current
74
74
  if (themeMap[key] !== undefined) return themeMap[key]
75
- const value = getValueByDotNotation(themes[state.current], themes[base], key)
75
+ const value = getValueByDotNotation(themes[state.current], themes[base] || null, key)
76
76
  if (value !== undefined) {
77
77
  // store the value for next time
78
78
  themeMap[key] = value
@@ -195,7 +195,7 @@ export const navigate = async function () {
195
195
  previousFocus = Focus.get()
196
196
 
197
197
  // set focus to the view that we're routing to
198
- focus ? Focus.set(focus) : Focus.set(view)
198
+ focus ? focus.$focus() : view.$focus()
199
199
 
200
200
  // apply before settings to holder element
201
201
  if (route.transition.before) {