@lightningjs/blits 2.0.0-beta.1 → 2.0.0-beta.11

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 (57) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/README.md +2 -0
  3. package/element-test-full.txt +0 -0
  4. package/index.d.ts +169 -41
  5. package/package.json +28 -8
  6. package/scripts/lint.js +17 -0
  7. package/scripts/prepublishOnly.js +15 -6
  8. package/scripts/prepush.js +25 -7
  9. package/src/announcer/announcer.js +54 -6
  10. package/src/announcer/speechSynthesis.js +156 -37
  11. package/src/application.js +98 -3
  12. package/src/component/base/methods.js +5 -2
  13. package/src/component/base/router.js +6 -0
  14. package/src/component/base/timeouts_intervals.js +56 -0
  15. package/src/component/setup/routes.js +5 -0
  16. package/src/component/setup/state.js +2 -0
  17. package/src/component.js +64 -24
  18. package/src/components/Circle.js +5 -3
  19. package/src/components/FPScounter.js +1591 -1577
  20. package/src/components/RouterView.js +141 -124
  21. package/src/components/Sprite.js +65 -8
  22. package/src/components/index.js +0 -2
  23. package/src/constants.js +17 -0
  24. package/src/engine.js +17 -0
  25. package/src/engines/L3/element.js +101 -7
  26. package/src/engines/L3/fontLoader.js +17 -0
  27. package/src/engines/L3/index.js +17 -0
  28. package/src/engines/L3/launch.js +4 -1
  29. package/src/engines/L3/shaderLoader.js +17 -0
  30. package/src/engines/index.js +17 -0
  31. package/src/{focus.js → focus/focus.js} +6 -18
  32. package/src/focus/helpers.js +31 -0
  33. package/src/focus/hover.js +94 -0
  34. package/src/helpers/fetchJson.js +4 -6
  35. package/src/launch.js +1 -0
  36. package/src/lib/codegenerator/generator.js +55 -9
  37. package/src/lib/lifecycle.js +34 -3
  38. package/src/lib/log.js +4 -4
  39. package/src/lib/precompiler/precompiler.js +23 -14
  40. package/src/lib/reactivity/effect.js +3 -2
  41. package/src/lib/reactivityguard/computedprops.js +113 -28
  42. package/src/lib/shaders/shaders.js +2 -1
  43. package/src/lib/symbols.js +19 -0
  44. package/src/lib/utils.js +17 -0
  45. package/src/plugin.js +17 -0
  46. package/src/plugins/appstate.d.ts +26 -0
  47. package/src/plugins/index.d.ts +28 -0
  48. package/src/plugins/language.d.ts +37 -0
  49. package/src/plugins/storage/localCookie.js +4 -6
  50. package/src/plugins/storage/storage.d.ts +31 -0
  51. package/src/plugins/theme.d.ts +43 -0
  52. package/src/plugins/theme.js +30 -3
  53. package/src/router/router.js +78 -14
  54. package/test-output.txt +0 -0
  55. package/vite/inject-dev-config.js +17 -0
  56. package/vite/preCompiler.js +15 -5
  57. package/vscode/data/template-attributes.json +335 -86
@@ -16,141 +16,158 @@
16
16
  */
17
17
 
18
18
  import Component from '../component.js'
19
- import Router from '../router/router.js'
19
+ import Router, { state as routerState } from '../router/router.js'
20
20
  import symbols from '../lib/symbols.js'
21
- import Focus from '../focus.js'
21
+ import Focus from '../focus/focus.js'
22
22
 
23
23
  let hashchangeHandler = null
24
24
 
25
+ /** @typedef {{ $input?: (event: any) => boolean, $focus?: (event: any) => void }} RouterViewParent */
26
+
25
27
  export default () =>
26
- Component('RouterView', {
27
- /* eslint-disable no-unused-vars */
28
- code: {
29
- render: function anonymous(parent, component, context, components, effect, getRaw, Log) {
30
- const elms = []
31
- const validAttributes = [
32
- 'parent',
33
- 'rotation',
34
- 'w',
35
- 'width',
36
- 'h',
37
- 'height',
38
- 'x',
39
- 'y',
40
- 'z',
41
- 'zIndex',
42
- 'color',
43
- 'src',
44
- 'texture',
45
- 'fit',
46
- 'rtt',
47
- 'mount',
48
- 'pivot',
49
- 'scale',
50
- 'show',
51
- 'alpha',
52
- 'rounded',
53
- 'border',
54
- 'shadow',
55
- 'shader',
56
- 'clipping',
57
- 'overflow',
58
- 'font',
59
- 'size',
60
- 'maxwidth',
61
- 'maxheight',
62
- 'maxlines',
63
- 'textoverflow',
64
- 'letterspacing',
65
- 'lineheight',
66
- 'contain',
67
- 'align',
68
- 'content',
69
- 'placement',
70
- 'inspector-data',
71
- ]
72
- const elementConfigs = []
73
- const forloops = []
74
- const props = []
75
- const created = []
76
- const effects = {}
77
- const skips = []
78
- let componentType
79
- let rootComponent = component
80
- let propData
81
- let slotComponent
82
- let inSlot = false
83
- let slotChildCounter = 0
84
- let cmps = []
85
- elementConfigs[0] = {}
28
+ Component(
29
+ 'RouterView',
30
+ /** @type {any} */ ({
31
+ /* eslint-disable no-unused-vars */
32
+ code: {
33
+ render: function anonymous(parent, component, context, components, effect, getRaw, Log) {
34
+ const elms = []
35
+ const validAttributes = [
36
+ 'parent',
37
+ 'rotation',
38
+ 'w',
39
+ 'width',
40
+ 'h',
41
+ 'height',
42
+ 'x',
43
+ 'y',
44
+ 'z',
45
+ 'zIndex',
46
+ 'color',
47
+ 'src',
48
+ 'texture',
49
+ 'fit',
50
+ 'rtt',
51
+ 'mount',
52
+ 'pivot',
53
+ 'scale',
54
+ 'show',
55
+ 'alpha',
56
+ 'rounded',
57
+ 'border',
58
+ 'shadow',
59
+ 'shader',
60
+ 'clipping',
61
+ 'overflow',
62
+ 'font',
63
+ 'size',
64
+ 'maxwidth',
65
+ 'maxheight',
66
+ 'maxlines',
67
+ 'textoverflow',
68
+ 'letterspacing',
69
+ 'lineheight',
70
+ 'contain',
71
+ 'align',
72
+ 'content',
73
+ 'placement',
74
+ 'inspector-data',
75
+ 'holder',
76
+ ]
77
+ const elementConfigs = []
78
+ const forloops = []
79
+ const props = []
80
+ const created = []
81
+ const effects = {}
82
+ const skips = []
83
+ let componentType
84
+ let rootComponent = component
85
+ let propData
86
+ let slotComponent
87
+ let inSlot = false
88
+ let slotChildCounter = 0
89
+ let cmps = []
90
+ elementConfigs[0] = {}
91
+ elementConfigs[0]['___wrapper'] = true
86
92
 
87
- elms[0] = this.element(
88
- { parent: parent || 'root' },
89
- inSlot === true ? slotComponent : component
90
- )
93
+ elms[0] = this.element(
94
+ { parent: parent || 'root' },
95
+ inSlot === true ? slotComponent : component
96
+ )
91
97
 
92
- elementConfigs[0]['w'] = parent.node.w * (100 / 100)
93
- elementConfigs[0]['h'] = parent.node.h * (100 / 100)
94
- elms[0].populate(elementConfigs[0])
98
+ elementConfigs[0]['w'] = parent.node.w * (100 / 100)
99
+ elementConfigs[0]['height'] = parent.node.h * (100 / 100)
100
+ elms[0].populate(elementConfigs[0])
95
101
 
96
- if (inSlot === true) {
97
- slotChildCounter -= 1
98
- }
102
+ if (inSlot === true) {
103
+ slotChildCounter -= 1
104
+ }
99
105
 
100
- return {
101
- elms,
102
- cleanup: () => {
103
- rootComponent = null
104
- propData = null
105
- slotComponent = null
106
- parent = null
107
- component = null
108
- cmps.length = 0
109
- elms.length = 0
110
- components.length = 0
111
- elementConfigs.length = 0
112
- forloops.length = 0
113
- props.length = 0
114
- skips.length = 0
115
- },
116
- skips,
117
- }
106
+ return {
107
+ elms,
108
+ cleanup: () => {
109
+ rootComponent = null
110
+ propData = null
111
+ slotComponent = null
112
+ parent = null
113
+ component = null
114
+ cmps.length = 0
115
+ elms.length = 0
116
+ components.length = 0
117
+ elementConfigs.length = 0
118
+ forloops.length = 0
119
+ props.length = 0
120
+ skips.length = 0
121
+ },
122
+ skips,
123
+ }
124
+ },
125
+ effects: [],
126
+ context: {},
118
127
  },
119
- effects: [],
120
- context: {},
121
- },
122
- state() {
123
- return {
124
- activeView: null,
125
- }
126
- },
127
- hooks: {
128
- async ready() {
129
- if (
130
- this[symbols.parent][symbols.routerHooks] &&
131
- this[symbols.parent][symbols.routerHooks].init
132
- ) {
133
- await this[symbols.parent][symbols.routerHooks].init.apply(this[symbols.parent])
128
+ state() {
129
+ return {
130
+ activeView: null,
134
131
  }
135
- hashchangeHandler = () => Router.navigate.apply(this)
136
- Router.navigate.apply(this)
137
- window.addEventListener('hashchange', hashchangeHandler)
138
- },
139
- destroy() {
140
- window.removeEventListener('hashchange', hashchangeHandler, false)
141
132
  },
142
- focus() {
143
- if (this.activeView && Focus.get() === this) {
144
- this.activeView.$focus()
145
- }
133
+ hooks: {
134
+ async ready() {
135
+ const parent = this[symbols.parent]
136
+ if (parent && parent[symbols.routerHooks] && parent[symbols.routerHooks].init) {
137
+ await parent[symbols.routerHooks].init.apply(parent)
138
+ }
139
+
140
+ hashchangeHandler = () => Router.navigate.apply(this)
141
+ Router.navigate.apply(this)
142
+ window.addEventListener('hashchange', hashchangeHandler)
143
+ },
144
+ destroy() {
145
+ window.removeEventListener('hashchange', hashchangeHandler, false)
146
+ },
147
+ focus() {
148
+ if (this.activeView && Focus.get() === this) {
149
+ this.activeView.$focus()
150
+ }
151
+ },
146
152
  },
147
- },
148
- input: {
149
- back(e) {
150
- const navigating = Router.back.call(this)
151
- if (navigating === false) {
152
- this[symbols.parent].$focus(e)
153
- }
153
+ input: {
154
+ back(e) {
155
+ const parent = this[symbols.parent]
156
+
157
+ if (routerState.backNavigation === false) {
158
+ if (parent && parent.$input) {
159
+ parent.$input(e)
160
+ }
161
+ return
162
+ }
163
+
164
+ const navigating = Router.back.call(this)
165
+ if (navigating === false) {
166
+ if (parent && parent.$focus) {
167
+ parent.$focus(e)
168
+ }
169
+ }
170
+ },
154
171
  },
155
- },
156
- })
172
+ })
173
+ )
@@ -65,6 +65,7 @@ export default () =>
65
65
  'content',
66
66
  'placement',
67
67
  'inspector-data',
68
+ 'holder',
68
69
  ]
69
70
  const elementConfigs = []
70
71
  const forloops = []
@@ -80,6 +81,7 @@ export default () =>
80
81
  let slotChildCounter = 0
81
82
  let cmps = []
82
83
  elementConfigs[0] = {}
84
+ elementConfigs[0]['___wrapper'] = true
83
85
 
84
86
  elms[0] = this.element(
85
87
  { parent: parent || 'root' },
@@ -90,7 +92,9 @@ export default () =>
90
92
  elementConfigs[0]['h'] = parent.node.h * (100 / 100)
91
93
  elementConfigs[0]['texture'] = component.texture
92
94
  elementConfigs[0]['color'] = component.color
93
- elementConfigs[0]['effects'] = component.effects
95
+ elementConfigs[0]['rounded'] = component.rounded
96
+ elementConfigs[0]['border'] = component.border
97
+ elementConfigs[0]['shadow'] = component.shadow
94
98
  elms[0].populate(elementConfigs[0])
95
99
 
96
100
  if (inSlot === true) {
@@ -123,6 +127,15 @@ export default () =>
123
127
  function anonymous(component, elms, context, components, rootComponent, skips, effect) {
124
128
  elms[0].set('color', component.color)
125
129
  },
130
+ function anonymous(component, elms, context, components, rootComponent, skips, effect) {
131
+ elms[0].set('rounded', component.rounded)
132
+ },
133
+ function anonymous(component, elms, context, components, rootComponent, skips, effect) {
134
+ elms[0].set('border', component.border)
135
+ },
136
+ function anonymous(component, elms, context, components, rootComponent, skips, effect) {
137
+ elms[0].set('shadow', component.shadow)
138
+ },
126
139
  ],
127
140
  context: {},
128
141
  },
@@ -131,6 +144,11 @@ export default () =>
131
144
  map: undefined,
132
145
  frame: undefined,
133
146
  color: undefined,
147
+ rounded: undefined,
148
+ border: undefined,
149
+ shadow: undefined,
150
+ '@loaded': undefined,
151
+ '@error': undefined,
134
152
  },
135
153
  state() {
136
154
  return {
@@ -139,6 +157,33 @@ export default () =>
139
157
  }
140
158
  },
141
159
 
160
+ hooks: {
161
+ ready() {
162
+ const loaded = this['@loaded']
163
+ if (loaded && typeof loaded === 'function') {
164
+ const cb = (payload) => loaded({ w: payload?.w, h: payload?.h }, this[symbols.wrapper])
165
+ this._loadedCb = cb
166
+ if (this.spriteTexture !== undefined && this.spriteTexture !== null) {
167
+ this.spriteTexture.on('loaded', cb)
168
+ }
169
+ }
170
+ const error = this['@error']
171
+ if (error && typeof error === 'function') {
172
+ const cb = (payload) => error(payload, this[symbols.wrapper])
173
+ this._failedCb = cb
174
+ if (this.spriteTexture !== undefined && this.spriteTexture !== null) {
175
+ this.spriteTexture.on('failed', cb)
176
+ }
177
+ }
178
+ },
179
+ destroy() {
180
+ if (this.spriteTexture !== undefined && this.spriteTexture !== null) {
181
+ if (this._loadedCb) this.spriteTexture.off('loaded', this._loadedCb)
182
+ if (this._failedCb) this.spriteTexture.off('failed', this._failedCb)
183
+ }
184
+ },
185
+ },
186
+
142
187
  computed: {
143
188
  texture() {
144
189
  // If no image - nothing to render
@@ -158,24 +203,36 @@ export default () =>
158
203
  this.spriteTexture === null ||
159
204
  this.currentSrc !== this.image
160
205
  ) {
206
+ const prevTexture = this.spriteTexture
207
+ if (prevTexture !== undefined && prevTexture !== null) {
208
+ if (this._loadedCb) prevTexture.off('loaded', this._loadedCb)
209
+ if (this._failedCb) prevTexture.off('failed', this._failedCb)
210
+ }
161
211
  this.spriteTexture = renderer.createTexture('ImageTexture', {
162
212
  src: this.image,
163
213
  })
164
214
  this.currentSrc = this.image
215
+ if (this._loadedCb) this.spriteTexture.on('loaded', this._loadedCb)
216
+ if (this._failedCb) this.spriteTexture.on('failed', this._failedCb)
165
217
  }
166
218
 
167
219
  // Resolve frame data from sprite map
168
220
  let options = null
169
221
  if (
170
- this.map !== undefined &&
171
222
  this.map !== null &&
172
- this.frame !== undefined &&
173
- this.frame !== null
223
+ this.map !== undefined &&
224
+ this.frame !== null &&
225
+ this.frame !== undefined
174
226
  ) {
175
- options =
176
- 'frames' in this.map
177
- ? Object.assign({}, this.map.defaults || {}, this.map.frames[this.frame])
178
- : this.map[this.frame]
227
+ if (
228
+ this.map.frames !== null &&
229
+ this.map.frames !== undefined &&
230
+ this.frame in this.map.frames
231
+ ) {
232
+ options = Object.assign({}, this.map.defaults || {}, this.map.frames[this.frame])
233
+ } else if (this.frame in this.map) {
234
+ options = this.map[this.frame]
235
+ }
179
236
  }
180
237
 
181
238
  // If no map but frame is object (manual subtexture)
@@ -18,11 +18,9 @@
18
18
  import Circle from './Circle.js'
19
19
  import RouterView from './RouterView.js'
20
20
  import Sprite from './Sprite.js'
21
- import FPScounter from './FPScounter.js'
22
21
 
23
22
  export default () => ({
24
23
  Circle: Circle(),
25
24
  RouterView: RouterView(),
26
25
  Sprite: Sprite(),
27
- FPScounter: FPScounter(),
28
26
  })
package/src/constants.js CHANGED
@@ -1,3 +1,20 @@
1
+ /*
2
+ * Copyright 2023 Comcast Cable Communications Management, LLC
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ *
15
+ * SPDX-License-Identifier: Apache-2.0
16
+ */
17
+
1
18
  export const DEFAULT_HOLD_TIMEOUT_MS = 50
2
19
 
3
20
  export const DEFAULT_KEYMAP = {
package/src/engine.js CHANGED
@@ -1 +1,18 @@
1
+ /*
2
+ * Copyright 2023 Comcast Cable Communications Management, LLC
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ *
15
+ * SPDX-License-Identifier: Apache-2.0
16
+ */
17
+
1
18
  export { L3 as default } from './engines/index.js'
@@ -24,6 +24,8 @@ import symbols from '../../lib/symbols.js'
24
24
  import Settings from '../../settings.js'
25
25
  import shaders from '../../lib/shaders/shaders.js'
26
26
 
27
+ const holderComponentMap = new WeakMap()
28
+
27
29
  /**
28
30
  * Creates a padding object from a value and direction.
29
31
  * @param {number|object|string|undefined} padding - The padding value.
@@ -210,15 +212,29 @@ const propsTransformer = {
210
212
  this.props['rotation'] = v * (Math.PI / 180)
211
213
  },
212
214
  set w(v) {
213
- if (v < 0) v = 0
214
- this.props['w'] = parsePercentage.call(this, v, 'w')
215
+ const parsed = parsePercentage.call(this, v, 'w')
216
+ this.props['w'] = parsed
217
+ if (
218
+ this.___wrapper === true &&
219
+ this.element.component.eol !== true &&
220
+ this.element.component[symbols.holder] !== undefined
221
+ ) {
222
+ this.element.component[symbols.holder].set('w', parsed)
223
+ }
215
224
  },
216
225
  set width(v) {
217
226
  this.w = v
218
227
  },
219
228
  set h(v) {
220
- if (v < 0) v = 0
221
- this.props['h'] = parsePercentage.call(this, v, 'h')
229
+ const parsed = parsePercentage.call(this, v, 'h')
230
+ this.props['h'] = parsed
231
+ if (
232
+ this.___wrapper === true &&
233
+ this.element.component.eol !== true &&
234
+ this.element.component[symbols.holder] !== undefined
235
+ ) {
236
+ this.element.component[symbols.holder].set('h', parsed)
237
+ }
222
238
  },
223
239
  set height(v) {
224
240
  this.h = v
@@ -509,6 +525,9 @@ const propsTransformer = {
509
525
  this.props['data'] = v
510
526
  }
511
527
  },
528
+ set holder(v) {
529
+ this.props['interactive'] = v
530
+ },
512
531
  }
513
532
 
514
533
  export const elementAttributes = Object.keys(propsTransformer)
@@ -564,6 +583,10 @@ const Element = {
564
583
  ? renderer.createTextNode({ ...textDefaults, ...this.props.props })
565
584
  : renderer.createNode(this.props.props)
566
585
 
586
+ if (this.props['holder'] === true) {
587
+ holderComponentMap.set(this.node, this.component)
588
+ }
589
+
567
590
  if (props['@loaded'] !== undefined && typeof props['@loaded'] === 'function') {
568
591
  this.node.on('loaded', (el, { type, dimensions }) => {
569
592
  props['@loaded']({ w: dimensions.w, h: dimensions.h, type }, this)
@@ -587,6 +610,39 @@ const Element = {
587
610
  })
588
611
  }
589
612
  },
613
+ /**
614
+ * Sets framework-provided inspector metadata
615
+ * Only sets if inspector is enabled and in dev mode
616
+ * @param {Object} data - Framework inspector metadata to merge
617
+ */
618
+ setInspectorMetadata(data) {
619
+ // Early return if inspector not enabled (performance optimization)
620
+ if (inspectorEnabled !== true) {
621
+ return
622
+ }
623
+
624
+ // Early return if element is destroyed (props.props is null)
625
+ if (this.props.props === undefined || this.props.props === null) {
626
+ return
627
+ }
628
+
629
+ // Initialize data object if it doesn't exist
630
+ if (this.props['data'] === undefined) {
631
+ this.props['data'] = {}
632
+ }
633
+ if (this.props.props['data'] === undefined) {
634
+ this.props.props['data'] = {}
635
+ }
636
+
637
+ // Merge framework data (blits-* keys data attributes)
638
+ Object.assign(this.props['data'], data)
639
+ Object.assign(this.props.props['data'], data)
640
+
641
+ // Sync to renderer node so inspector can see it
642
+ if (this.node !== undefined && this.node !== null) {
643
+ this.node.data = { ...this.props.props['data'] }
644
+ }
645
+ },
590
646
  /**
591
647
  * Set an individual property on the node
592
648
  *
@@ -632,13 +688,31 @@ const Element = {
632
688
  }
633
689
  },
634
690
  animate(prop, value, transition) {
691
+ // Clear any existing debounce timeout for this property
692
+ if (this.debounceTimeouts[prop] !== undefined) {
693
+ clearTimeout(this.debounceTimeouts[prop])
694
+ Log.debug(`Cleared debounce timeout for property "${prop}"`)
695
+ }
696
+
697
+ // Debounce the animation execution
698
+ this.debounceTimeouts[prop] = setTimeout(() => {
699
+ delete this.debounceTimeouts[prop]
700
+ this._executeAnimation(prop, value, transition)
701
+ }, 0)
702
+ },
703
+ _executeAnimation(prop, value, transition) {
635
704
  // check if a transition is already scheduled to run on the same prop
636
705
  // and cancels it if it does
706
+ const stateOfAnimation =
707
+ this.scheduledTransitions[prop] !== undefined
708
+ ? this.scheduledTransitions[prop].f.state
709
+ : undefined
710
+
637
711
  if (
638
- this.scheduledTransitions[prop] !== undefined &&
639
- this.scheduledTransitions[prop].f.state === 'scheduled'
712
+ stateOfAnimation !== undefined &&
713
+ (stateOfAnimation === 'scheduled' || stateOfAnimation === 'running')
640
714
  ) {
641
- this.scheduledTransitions[prop].f.stop()
715
+ this.scheduledTransitions[prop].f.stop(stateOfAnimation === 'running' ? false : true)
642
716
  }
643
717
 
644
718
  // if current value is the same as the value to animate to, instantly resolve
@@ -674,6 +748,11 @@ const Element = {
674
748
  f,
675
749
  }
676
750
 
751
+ // Update inspector metadata when transition starts
752
+ if (inspectorEnabled === true) {
753
+ this.setInspectorMetadata({ 'blits-isTransitioning': true })
754
+ }
755
+
677
756
  if (transition.start !== undefined && typeof transition.start === 'function') {
678
757
  // fire transition start callback when animation really starts (depending on specified delay)
679
758
  f.once('animating', () => {
@@ -708,6 +787,12 @@ const Element = {
708
787
  }
709
788
  // remove the prop from scheduled transitions
710
789
  delete this.scheduledTransitions[prop]
790
+ // Update inspector metadata when transition ends
791
+ if (inspectorEnabled === true) {
792
+ this.setInspectorMetadata({
793
+ 'blits-isTransitioning': Object.keys(this.scheduledTransitions).length > 0,
794
+ })
795
+ }
711
796
  })
712
797
 
713
798
  // start animation
@@ -717,6 +802,14 @@ const Element = {
717
802
  if (this.node === null) return
718
803
 
719
804
  Log.debug('Deleting Node', this.nodeId)
805
+
806
+ // Clear all pending debounce timeouts
807
+ const debounceProps = Object.keys(this.debounceTimeouts)
808
+ for (let i = 0; i < debounceProps.length; i++) {
809
+ clearTimeout(this.debounceTimeouts[debounceProps[i]])
810
+ }
811
+ this.debounceTimeouts = null
812
+
720
813
  // Clearing transition end callback functions
721
814
  const transitionProps = Object.keys(this.scheduledTransitions)
722
815
  for (let i = 0; i < transitionProps.length; i++) {
@@ -799,6 +892,7 @@ export default (config, component) => {
799
892
  return Object.assign(Object.create(Element), {
800
893
  props: Object.assign(Object.create(propsTransformer), { props: {} }),
801
894
  scheduledTransitions: {},
895
+ debounceTimeouts: {},
802
896
  config,
803
897
  component,
804
898
  })
@@ -1,3 +1,20 @@
1
+ /*
2
+ * Copyright 2023 Comcast Cable Communications Management, LLC
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ *
15
+ * SPDX-License-Identifier: Apache-2.0
16
+ */
17
+
1
18
  import Settings from '../../settings.js'
2
19
  import { renderer } from './launch.js'
3
20