@lightningjs/blits 1.30.0 → 1.30.2

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 (149) hide show
  1. package/.editorconfig +7 -7
  2. package/.eslintignore +1 -1
  3. package/.eslintrc.cjs +65 -65
  4. package/.github/workflows/comment-lint-results.yml +124 -124
  5. package/.github/workflows/comment-test-results.yml +108 -108
  6. package/.github/workflows/deploy-docs.yml +27 -27
  7. package/.github/workflows/run-blits-tests.yml +141 -141
  8. package/.github/workflows/run-lint.yml +127 -127
  9. package/.husky/pre-commit +1 -1
  10. package/.vscode/extensions.json +7 -7
  11. package/.vscode/settings.json +9 -9
  12. package/CHANGELOG.md +1019 -1005
  13. package/COPYING +1 -0
  14. package/LICENSE +202 -202
  15. package/NOTICE +12 -12
  16. package/README.md +33 -33
  17. package/bin/index.js +8 -8
  18. package/blits.d.ts +6 -6
  19. package/docs/README.md +19 -19
  20. package/docs/_sidebar.md +37 -37
  21. package/docs/built-in/directives.md +50 -50
  22. package/docs/built-in/for-loop.md +203 -203
  23. package/docs/built-in/layout.md +168 -168
  24. package/docs/components/component_state.md +90 -90
  25. package/docs/components/computed_properties.md +45 -45
  26. package/docs/components/lifecycle_events.md +42 -42
  27. package/docs/components/methods.md +45 -45
  28. package/docs/components/props.md +56 -56
  29. package/docs/components/user_input.md +186 -186
  30. package/docs/components/utility_methods.md +320 -320
  31. package/docs/components/watchers.md +34 -34
  32. package/docs/essentials/application_root.md +19 -19
  33. package/docs/essentials/components.md +68 -68
  34. package/docs/essentials/displaying_images.md +67 -67
  35. package/docs/essentials/displaying_text.md +135 -135
  36. package/docs/essentials/element_attributes.md +195 -195
  37. package/docs/essentials/settings.md +117 -117
  38. package/docs/essentials/template_syntax.md +83 -83
  39. package/docs/getting_started/file_structure.md +58 -58
  40. package/docs/getting_started/getting_started.md +45 -45
  41. package/docs/getting_started/intro.md +20 -20
  42. package/docs/index.html +47 -47
  43. package/docs/performance/lazy-loading.md +161 -161
  44. package/docs/plugins/global_app_state.md +98 -98
  45. package/docs/plugins/language.md +172 -172
  46. package/docs/plugins/text-to-speech-announcer.md +119 -119
  47. package/docs/plugins/theme.md +176 -176
  48. package/docs/router/basics.md +175 -175
  49. package/docs/router/hooks.md +70 -70
  50. package/docs/router/transitions.md +146 -146
  51. package/docs/sidebar.json +171 -171
  52. package/docs/transitions_animations/transitions.md +150 -150
  53. package/index.d.ts +1099 -1099
  54. package/index.js +28 -28
  55. package/jsconfig.json +12 -12
  56. package/package.json +56 -56
  57. package/public/assets/fps_sprite.base64.js +1 -1
  58. package/scripts/postpublish.js +59 -59
  59. package/scripts/prepublishOnly.js +84 -84
  60. package/scripts/prepush.js +78 -78
  61. package/src/announcer/announcer.js +162 -162
  62. package/src/announcer/speechSynthesis.js +107 -107
  63. package/src/application.js +100 -100
  64. package/src/component/base/announcer.js +27 -27
  65. package/src/component/base/events.js +69 -69
  66. package/src/component/base/index.js +33 -33
  67. package/src/component/base/methods.js +244 -244
  68. package/src/component/base/router.js +41 -41
  69. package/src/component/base/timeouts_intervals.js +99 -99
  70. package/src/component/base/utils.js +72 -72
  71. package/src/component/setup/computed.js +48 -48
  72. package/src/component/setup/index.js +62 -62
  73. package/src/component/setup/input.js +30 -30
  74. package/src/component/setup/methods.js +37 -37
  75. package/src/component/setup/props.js +62 -62
  76. package/src/component/setup/props.test.js +238 -238
  77. package/src/component/setup/routes.js +42 -42
  78. package/src/component/setup/state.js +59 -59
  79. package/src/component/setup/watch.js +35 -35
  80. package/src/component.js +437 -437
  81. package/src/component.test.js +388 -388
  82. package/src/components/Circle.js +73 -73
  83. package/src/components/Circle.orig.js +31 -31
  84. package/src/components/FPScounter.js +1510 -1510
  85. package/src/components/FPScounter.orig.js +121 -121
  86. package/src/components/RouterView.js +81 -81
  87. package/src/components/RouterView.orig.js +54 -54
  88. package/src/components/Sprite.js +94 -94
  89. package/src/components/Sprite.orig.js +58 -58
  90. package/src/components/index.js +28 -28
  91. package/src/components/index.orig.js +28 -28
  92. package/src/constants.js +21 -21
  93. package/src/engine.js +1 -1
  94. package/src/engines/L3/element.js +744 -744
  95. package/src/engines/L3/element.test.js +579 -579
  96. package/src/engines/L3/fontLoader.js +38 -38
  97. package/src/engines/L3/index.js +7 -7
  98. package/src/engines/L3/launch.js +123 -123
  99. package/src/engines/L3/shaderLoader.js +12 -12
  100. package/src/engines/index.js +1 -1
  101. package/src/focus.js +90 -90
  102. package/src/helpers/deepEqualArray.js +50 -50
  103. package/src/helpers/deepEqualArray.test.js +127 -127
  104. package/src/helpers/fetchJson.js +34 -34
  105. package/src/helpers/sequence.js +26 -26
  106. package/src/launch.js +123 -123
  107. package/src/lib/blitsfileconverter/blitsfileconverter.js +52 -52
  108. package/src/lib/codegenerator/generator.js +788 -788
  109. package/src/lib/codegenerator/generator.test.js +3801 -3801
  110. package/src/lib/colors/colors.js +92 -92
  111. package/src/lib/colors/colors.test.js +290 -290
  112. package/src/lib/colors/htmlColors.js +168 -168
  113. package/src/lib/componentId.js +49 -49
  114. package/src/lib/componentId.test.js +84 -84
  115. package/src/lib/eventListeners.js +123 -123
  116. package/src/lib/eventListeners.test.js +236 -236
  117. package/src/lib/hooks.js +59 -59
  118. package/src/lib/lifecycle.js +85 -85
  119. package/src/lib/log.js +117 -117
  120. package/src/lib/precompiler/precompiler.js +73 -73
  121. package/src/lib/reactivity/effect.js +93 -93
  122. package/src/lib/reactivity/effect.test.js +35 -35
  123. package/src/lib/reactivity/reactive.js +208 -208
  124. package/src/lib/reactivity/reactive.test.js +27 -27
  125. package/src/lib/reactivityguard/computedprops.js +218 -218
  126. package/src/lib/symbols.js +108 -108
  127. package/src/lib/templateparser/parser.js +386 -386
  128. package/src/lib/templateparser/parser.test.js +1411 -1411
  129. package/src/lib/utils.js +27 -27
  130. package/src/plugin.js +27 -27
  131. package/src/plugins/appstate.js +26 -26
  132. package/src/plugins/index.js +20 -20
  133. package/src/plugins/language.js +152 -152
  134. package/src/plugins/log.js +25 -25
  135. package/src/plugins/theme.js +94 -94
  136. package/src/router/router.js +514 -514
  137. package/src/router/router.test.js +473 -473
  138. package/src/router/transitions/fadeIn.js +27 -27
  139. package/src/router/transitions/fadeInOut.js +33 -33
  140. package/src/router/transitions/index.js +21 -21
  141. package/src/router/transitions/slideIn.js +28 -28
  142. package/src/router/transitions/slideInOut.js +33 -33
  143. package/src/settings.js +40 -40
  144. package/vite/blitsFileConverter.js +67 -67
  145. package/vite/index.js +28 -28
  146. package/vite/msdfGenerator.js +269 -269
  147. package/vite/preCompiler.js +42 -42
  148. package/vite/reactivityGuard.js +45 -45
  149. package/vscode/data/template-attributes.json +589 -589
@@ -1,320 +1,320 @@
1
- # Utility methods
2
-
3
- Besides the methods that are specified in the Component configuration object, each Blits Component comes with a number of
4
- _utility methods_ built in.
5
-
6
- Utility methods are prefixed with a `$`-sign, to clearly indicate they are _framework provided_ methods, that are _not private_, but
7
- intended to be _used by the developer_. The `$`-prefix also helps prevent accidental naming collisions.
8
-
9
- ### $select
10
-
11
- `this.$select()` is used to search for child Elements and Components by reference (`ref`).
12
-
13
- The `$select()`-method accepts a string pointing to a reference as it's only argument, and returns the Element or Component that
14
- matches that `ref`. In order to make an Element or Component findable, the `ref`-attribute should be provided on said Element of Component
15
- in the template.
16
-
17
- ```js
18
- export default Blits.Component('MyComponent', {
19
- components: {
20
- Button,
21
- },
22
- template: `
23
- <Element>
24
- <Element w="1920" h="5" color="green" ref="topBorder" />
25
- <Button text="Button 1" ref="btn1" y="100" />
26
- <Button text="Button 2" ref="btn2" y="200" />
27
- <Button text="Button 3" ref="btn3" y="200" />
28
- </Element>
29
- `,
30
- hooks: {
31
- ready() {
32
- const topBorderElement = this.$select('topBorder')
33
- const button2 = this.$select('btn2')
34
- }
35
- }
36
- })
37
- ```
38
-
39
- > As the `this.$select()` gives direct access to an Element or a Component, it allows the developer to directly interact with it. There are valid use cases, but direct manipulation of Elements, calling methods and changing properties on a Component comes with risks. It circumvents the whole structure of reactivity and props communication built into Blits, and can lead to unexpected situations or anti-patterns. So use with care!
40
-
41
- ### $focus
42
-
43
- The `$focus()`-method can be used to set the focus to a Component instance. It can either be called on the instance itself, in order to "claim"
44
- the focus. But more often it will be used to delegate the focus from on Component to another.
45
-
46
- > It is important to understand that only a Component can receive focus in Blits. Elements don't have a focus method, and also have no way of reacting to focus changes (because they have no hooks etc.).
47
-
48
- Often focus will be passed onto a child component, for example in the case of a list or swimlane, where you want to delegate the focus to a poster.
49
- In this case the focus method will be used in combination with `this.$select()` to first select the desired child Component by ref, and subsequently call the `$focus()` method on that Component.
50
-
51
- ```js
52
- export default Blits.Component('MyComponent', {
53
- components: {
54
- Button,
55
- },
56
- template: `
57
- <Element>
58
- <Element w="1920" h="5" color="green" ref="topBorder" />
59
- <Button text="Button 1" ref="btn1" y="100" />
60
- <Button text="Button 2" ref="btn2" y="200" />
61
- <Button text="Button 3" ref="btn3" y="200" />
62
- </Element>
63
- `,
64
- state() {
65
- focusIndex: 1
66
- },
67
- input: {
68
- down() {
69
- this.focusIndex--
70
- const button = this.$select(`btn${this.focusIndex}`)
71
- if(button) {
72
- button.$focus()
73
- }
74
- },
75
- up() {
76
- this.focusIndex++
77
- const button = this.$select(`btn${this.focusIndex}`)
78
- if(button) {
79
- button.$focus()
80
- }
81
- },
82
- }
83
- })
84
- ```
85
-
86
- Another frequent case is that focus is passed on to the parent Component. The parent component is available on the Component scope as `this.parent`. So passing the focus to the parent is as simple as calling `this.parent.$focus()`.
87
-
88
- The `$focus`-method accepts an optional `event` parameter, which is of the type `KeyboardEvent`. When the `event` parameter is provided, not only will the selected Component receive focus, but the input event will be emitted again on the component that just received focus.
89
-
90
- This can be used to _bubble up_ input events (specified in the `input` key of the component configuration object) and helps to create a smooth experience, preventing a user to click multiple times.
91
-
92
-
93
- ### $trigger
94
-
95
- Blits has reactivity built-in, which means that re-renders in the template, as well as watchers and computed properties, are automatically fired whenever a state value changes. But sometimes you may want trigger the side effects of a state values change, without actually changing the value. This often is useful when a Component first receives focus and you want to ensure that the default state values are applied.
96
-
97
- Instead of setting a value to `null` and then setting it back to the initial value (which would trigger side effects), the `this.$trigger()` method can be used. The method accepts the name of the state variable as its argument.
98
-
99
- ```js
100
- export default Blits.Component('MyComponent', {
101
- state() {
102
- focusIndex: 1
103
- },
104
- watchers: {
105
- focusIndex(v) {
106
- console.log('I trigger when focusIndex changes')
107
- }
108
- },
109
- hooks: {
110
- focus() {
111
- // even though focusIndex didn't change
112
- // the watcher on focusIndex will be fired
113
- this.$trigger('focusIndex')
114
- }
115
- }
116
- })
117
- ```
118
-
119
- ## Emitting events and passing data
120
-
121
- ### $emit
122
-
123
- When working with Components in Blits you will often want to send data from one to another. In the case of (direct) child Component, using
124
- props is the defacto way for inter-component communication.
125
-
126
- For passing data from a child to a parent component, you may be tempted to use the `this.parent` reference and change the state directly. While this works, it does create a strict dependency on the parent, meaning the child Component only works properly when tied directly to a specific parent. This reduces reusability of Components and may cause limitations or problems later on.
127
-
128
- Instead the `this.$emit()` method can be used, which is available on each Component as a utility function. It's designed to easily emit data to anywhere in an App. The first argument is the `name` of the event that will be emitted (i.e. `changeBackground`) and optionally a second argument with additional `data` can be passed.
129
-
130
- ```js
131
- export default Blits.Component('MyComponent', {
132
- input: {
133
- enter() {
134
- // sending a activate event, without any additional data
135
- this.$emit('activate')
136
- },
137
- space() {
138
- // sending a changeBackground event, with additional data
139
- this.$emit('changeBackground', {
140
- img: './assets/background1.jpg',
141
- delay: 400
142
- })
143
- }
144
- }
145
- })
146
- ```
147
-
148
- ### $listen
149
-
150
- The `$listen` utility method (which is available on each Blits Component) is designed to listen for events emitted by `this.$emit()`.
151
- Any Component can subscribe to any event emitted and as such a flexible system of passing data around can be constructed.
152
-
153
- The first argument of the `$listen()`-method is the event `name` to listen for, and the second argument is a `callback` to be executed
154
- anytime the event occurs. When the event is emitted with additional data, it's passed as the first argument in the callback.
155
-
156
- Generally the listeners are registered in the Component `ready` or `init` hook. Blits automatically takes care of deregistering the listeners whenever a Component is destroyed.
157
-
158
- ```js
159
- export default Blits.Component('MyComponent', {
160
- hooks: {
161
- init() {
162
- // activate event without any additional value
163
- this.$listen('activate', () => {
164
- console.log('activated')
165
- // set a text state valuye
166
- this.text = 'We are active!'
167
- })
168
-
169
- // change background event
170
- this.$listen('changeBackground', (data) => {
171
- setTimeout(() => {
172
- this.backgroundImage = data.img
173
- }, data.delay)
174
- })
175
- },
176
- }
177
- })
178
- ```
179
-
180
- ### $unlisten
181
-
182
- The `$unlisten` utility method (which is available on each Blits Component) is designed to remove event listeners that were previously registered using `$listen()`. This method helps in managing event listeners manually when needed.
183
-
184
- The first argument of the `$unlisten()` method is the event `name` to stop listening for. This ensures that all listeners for the specified event are removed from the component.
185
-
186
- Generally, you might not need to call `$unlisten()` manually, as Blits automatically deregisters listeners when a Component is destroyed. However, it can be useful in scenarios where you need to stop listening for events before the Component is destroyed.
187
-
188
- ```js
189
- export default Blits.Component('MyComponent', {
190
- hooks: {
191
- init() {
192
- this.activateListener = () => {
193
- console.log('activated')
194
- this.text = 'We are active!'
195
- }
196
-
197
- this.changeBackgroundListener = (data) => {
198
- setTimeout(() => {
199
- this.backgroundImage = data.img
200
- }, data.delay)
201
- }
202
-
203
- // Register listeners
204
- this.$listen('activate', this.activateListener)
205
- this.$listen('changeBackground', this.changeBackgroundListener)
206
- },
207
- unfocus() {
208
- // Remove listeners when Component is unfocused
209
- this.$unlisten('activate')
210
- this.$unlisten('changeBackground')
211
- }
212
- }
213
- })
214
- ```
215
-
216
- ## Timeouts
217
-
218
- Setting a timeout is a typical way in JS to execute functionality with a delay. But timeouts can also be a common cause of
219
- memory leaks.
220
-
221
- As long as the timeout is still pending it, it will hold on to references which can prevent a proper garbage collection from happening. That's why it's important to clear timeouts when destroying a component.
222
-
223
- Blits comes with some built-in methods to register and clear timeouts. Under the hood these methods use regular timeouts, but using the Blits built-in methods ensures that timeouts properly are cleared whenever a component is being disposed.
224
-
225
- ### $setTimeout
226
-
227
- The `this.$setTimeout()`-method creates a timeout, using a thin wrapper around the `window.setTimeout()` method. The first argument is the `callback` to be executed after the `delay` specified in the second argument. The method returns a `timeout id`, which can be used to mannually clear the timeout.
228
-
229
- ### $clearTimeout
230
-
231
- The `this.$clearTimeout()`-method is a thin wrapper around the `window.clearTimeout()`-method. By passing in the `id` of a timeout, it will clear and remove the timeout.
232
-
233
- ### $clearTimeouts
234
-
235
- The `this.$clearTimeouts()`-method is a utility method that is used to clear all the timeouts created via the `this.$setTimeout()` method in one go. The method doesn't require to pass any timeout ids. This method is automatically called when destroying a Component, and with that it prevents memory leaks due to dangling timeouts.
236
-
237
- ```js
238
- export default Blits.Component('MyComponent', {
239
- state() {
240
- timeout: null
241
- },
242
- hooks: {
243
- init() {
244
- this.timeout = this.$setTimeout(() => {
245
- console.log('1 minute has passed!')
246
- }, 60000)
247
-
248
- this.$setTimeout(() => {
249
- console.log('10 seconds have passed!')
250
- }, 10000)
251
- },
252
- unfocus() {
253
- // clear all timeouts when Component is unfocused
254
- this.$clearTimeouts()
255
- }
256
- },
257
- input: {
258
- enter() {
259
- // clear the timeout
260
- this.$clearTimeout(this.timeout)
261
- }
262
- }
263
- })
264
- ```
265
-
266
- ## Intervals
267
-
268
- Timeouts are a great way to execute recurring logic, such as polling for updates or periodically updating an animation.
269
-
270
- But similar to timeouts, intervals (more specifically failing to clear them) are a common source of memory leaks - maybe even more than dangling timeouts.
271
-
272
- For this reason Blits has 3 built-in utility methods to help registering and clearing intervals.
273
-
274
- ### $setInterval
275
-
276
- The `this.$setInterval()`-method create an interval, using a thin wrapper around the `window.setInterval()` method. The first argument is the `callback` to be executed after each interval `delay` specified in the second argument. The method returns an `interval id`, which can be used to manually clear the interval.
277
-
278
- ### $clearInterval
279
-
280
- The `this.$clearInterval()`-method is a thin wrapper around the `window.clearInterval()`-method. By passing in the `id` of an interval, it will clear and remove the interval.
281
-
282
-
283
- ### $clearIntervals
284
-
285
- The `this.$clearIntervals()`-method is a utility method that is used to clear all the intervals created via the `this.$setInterval()` method in one go. The method doesn't require to pass any interval ids. This method is automatically called when destroying a Component, and with that it prevents memory leaks due to dangling intervals.
286
-
287
- ```js
288
- export default Blits.Component('MyComponent', {
289
- state() {
290
- return {
291
- interval: null,
292
- secondCount: 0,
293
- minuteCount: 0,
294
- }
295
- },
296
- hooks: {
297
- init() {
298
- this.interval = this.$setInterval(() => {
299
- this.secondCount++
300
- console.log(`${this.secondCount} second(s) have passed`)
301
- }, 1000)
302
-
303
- this.$setInterval(() => {
304
- this.minuteCount++
305
- console.log(`${this.minuteCount} minute(s) have passed`)
306
- }, 60000)
307
- },
308
- unfocus() {
309
- // clear all intervals when Component is unfocused
310
- this.$clearIntervals()
311
- }
312
- },
313
- input: {
314
- enter() {
315
- // clear the interval
316
- this.$clearInterval(this.interval)
317
- }
318
- }
319
- })
320
- ```
1
+ # Utility methods
2
+
3
+ Besides the methods that are specified in the Component configuration object, each Blits Component comes with a number of
4
+ _utility methods_ built in.
5
+
6
+ Utility methods are prefixed with a `$`-sign, to clearly indicate they are _framework provided_ methods, that are _not private_, but
7
+ intended to be _used by the developer_. The `$`-prefix also helps prevent accidental naming collisions.
8
+
9
+ ### $select
10
+
11
+ `this.$select()` is used to search for child Elements and Components by reference (`ref`).
12
+
13
+ The `$select()`-method accepts a string pointing to a reference as it's only argument, and returns the Element or Component that
14
+ matches that `ref`. In order to make an Element or Component findable, the `ref`-attribute should be provided on said Element of Component
15
+ in the template.
16
+
17
+ ```js
18
+ export default Blits.Component('MyComponent', {
19
+ components: {
20
+ Button,
21
+ },
22
+ template: `
23
+ <Element>
24
+ <Element w="1920" h="5" color="green" ref="topBorder" />
25
+ <Button text="Button 1" ref="btn1" y="100" />
26
+ <Button text="Button 2" ref="btn2" y="200" />
27
+ <Button text="Button 3" ref="btn3" y="200" />
28
+ </Element>
29
+ `,
30
+ hooks: {
31
+ ready() {
32
+ const topBorderElement = this.$select('topBorder')
33
+ const button2 = this.$select('btn2')
34
+ }
35
+ }
36
+ })
37
+ ```
38
+
39
+ > As the `this.$select()` gives direct access to an Element or a Component, it allows the developer to directly interact with it. There are valid use cases, but direct manipulation of Elements, calling methods and changing properties on a Component comes with risks. It circumvents the whole structure of reactivity and props communication built into Blits, and can lead to unexpected situations or anti-patterns. So use with care!
40
+
41
+ ### $focus
42
+
43
+ The `$focus()`-method can be used to set the focus to a Component instance. It can either be called on the instance itself, in order to "claim"
44
+ the focus. But more often it will be used to delegate the focus from on Component to another.
45
+
46
+ > It is important to understand that only a Component can receive focus in Blits. Elements don't have a focus method, and also have no way of reacting to focus changes (because they have no hooks etc.).
47
+
48
+ Often focus will be passed onto a child component, for example in the case of a list or swimlane, where you want to delegate the focus to a poster.
49
+ In this case the focus method will be used in combination with `this.$select()` to first select the desired child Component by ref, and subsequently call the `$focus()` method on that Component.
50
+
51
+ ```js
52
+ export default Blits.Component('MyComponent', {
53
+ components: {
54
+ Button,
55
+ },
56
+ template: `
57
+ <Element>
58
+ <Element w="1920" h="5" color="green" ref="topBorder" />
59
+ <Button text="Button 1" ref="btn1" y="100" />
60
+ <Button text="Button 2" ref="btn2" y="200" />
61
+ <Button text="Button 3" ref="btn3" y="200" />
62
+ </Element>
63
+ `,
64
+ state() {
65
+ focusIndex: 1
66
+ },
67
+ input: {
68
+ down() {
69
+ this.focusIndex--
70
+ const button = this.$select(`btn${this.focusIndex}`)
71
+ if(button) {
72
+ button.$focus()
73
+ }
74
+ },
75
+ up() {
76
+ this.focusIndex++
77
+ const button = this.$select(`btn${this.focusIndex}`)
78
+ if(button) {
79
+ button.$focus()
80
+ }
81
+ },
82
+ }
83
+ })
84
+ ```
85
+
86
+ Another frequent case is that focus is passed on to the parent Component. The parent component is available on the Component scope as `this.parent`. So passing the focus to the parent is as simple as calling `this.parent.$focus()`.
87
+
88
+ The `$focus`-method accepts an optional `event` parameter, which is of the type `KeyboardEvent`. When the `event` parameter is provided, not only will the selected Component receive focus, but the input event will be emitted again on the component that just received focus.
89
+
90
+ This can be used to _bubble up_ input events (specified in the `input` key of the component configuration object) and helps to create a smooth experience, preventing a user to click multiple times.
91
+
92
+
93
+ ### $trigger
94
+
95
+ Blits has reactivity built-in, which means that re-renders in the template, as well as watchers and computed properties, are automatically fired whenever a state value changes. But sometimes you may want trigger the side effects of a state values change, without actually changing the value. This often is useful when a Component first receives focus and you want to ensure that the default state values are applied.
96
+
97
+ Instead of setting a value to `null` and then setting it back to the initial value (which would trigger side effects), the `this.$trigger()` method can be used. The method accepts the name of the state variable as its argument.
98
+
99
+ ```js
100
+ export default Blits.Component('MyComponent', {
101
+ state() {
102
+ focusIndex: 1
103
+ },
104
+ watchers: {
105
+ focusIndex(v) {
106
+ console.log('I trigger when focusIndex changes')
107
+ }
108
+ },
109
+ hooks: {
110
+ focus() {
111
+ // even though focusIndex didn't change
112
+ // the watcher on focusIndex will be fired
113
+ this.$trigger('focusIndex')
114
+ }
115
+ }
116
+ })
117
+ ```
118
+
119
+ ## Emitting events and passing data
120
+
121
+ ### $emit
122
+
123
+ When working with Components in Blits you will often want to send data from one to another. In the case of (direct) child Component, using
124
+ props is the defacto way for inter-component communication.
125
+
126
+ For passing data from a child to a parent component, you may be tempted to use the `this.parent` reference and change the state directly. While this works, it does create a strict dependency on the parent, meaning the child Component only works properly when tied directly to a specific parent. This reduces reusability of Components and may cause limitations or problems later on.
127
+
128
+ Instead the `this.$emit()` method can be used, which is available on each Component as a utility function. It's designed to easily emit data to anywhere in an App. The first argument is the `name` of the event that will be emitted (i.e. `changeBackground`) and optionally a second argument with additional `data` can be passed.
129
+
130
+ ```js
131
+ export default Blits.Component('MyComponent', {
132
+ input: {
133
+ enter() {
134
+ // sending a activate event, without any additional data
135
+ this.$emit('activate')
136
+ },
137
+ space() {
138
+ // sending a changeBackground event, with additional data
139
+ this.$emit('changeBackground', {
140
+ img: './assets/background1.jpg',
141
+ delay: 400
142
+ })
143
+ }
144
+ }
145
+ })
146
+ ```
147
+
148
+ ### $listen
149
+
150
+ The `$listen` utility method (which is available on each Blits Component) is designed to listen for events emitted by `this.$emit()`.
151
+ Any Component can subscribe to any event emitted and as such a flexible system of passing data around can be constructed.
152
+
153
+ The first argument of the `$listen()`-method is the event `name` to listen for, and the second argument is a `callback` to be executed
154
+ anytime the event occurs. When the event is emitted with additional data, it's passed as the first argument in the callback.
155
+
156
+ Generally the listeners are registered in the Component `ready` or `init` hook. Blits automatically takes care of deregistering the listeners whenever a Component is destroyed.
157
+
158
+ ```js
159
+ export default Blits.Component('MyComponent', {
160
+ hooks: {
161
+ init() {
162
+ // activate event without any additional value
163
+ this.$listen('activate', () => {
164
+ console.log('activated')
165
+ // set a text state valuye
166
+ this.text = 'We are active!'
167
+ })
168
+
169
+ // change background event
170
+ this.$listen('changeBackground', (data) => {
171
+ setTimeout(() => {
172
+ this.backgroundImage = data.img
173
+ }, data.delay)
174
+ })
175
+ },
176
+ }
177
+ })
178
+ ```
179
+
180
+ ### $unlisten
181
+
182
+ The `$unlisten` utility method (which is available on each Blits Component) is designed to remove event listeners that were previously registered using `$listen()`. This method helps in managing event listeners manually when needed.
183
+
184
+ The first argument of the `$unlisten()` method is the event `name` to stop listening for. This ensures that all listeners for the specified event are removed from the component.
185
+
186
+ Generally, you might not need to call `$unlisten()` manually, as Blits automatically deregisters listeners when a Component is destroyed. However, it can be useful in scenarios where you need to stop listening for events before the Component is destroyed.
187
+
188
+ ```js
189
+ export default Blits.Component('MyComponent', {
190
+ hooks: {
191
+ init() {
192
+ this.activateListener = () => {
193
+ console.log('activated')
194
+ this.text = 'We are active!'
195
+ }
196
+
197
+ this.changeBackgroundListener = (data) => {
198
+ setTimeout(() => {
199
+ this.backgroundImage = data.img
200
+ }, data.delay)
201
+ }
202
+
203
+ // Register listeners
204
+ this.$listen('activate', this.activateListener)
205
+ this.$listen('changeBackground', this.changeBackgroundListener)
206
+ },
207
+ unfocus() {
208
+ // Remove listeners when Component is unfocused
209
+ this.$unlisten('activate')
210
+ this.$unlisten('changeBackground')
211
+ }
212
+ }
213
+ })
214
+ ```
215
+
216
+ ## Timeouts
217
+
218
+ Setting a timeout is a typical way in JS to execute functionality with a delay. But timeouts can also be a common cause of
219
+ memory leaks.
220
+
221
+ As long as the timeout is still pending it, it will hold on to references which can prevent a proper garbage collection from happening. That's why it's important to clear timeouts when destroying a component.
222
+
223
+ Blits comes with some built-in methods to register and clear timeouts. Under the hood these methods use regular timeouts, but using the Blits built-in methods ensures that timeouts properly are cleared whenever a component is being disposed.
224
+
225
+ ### $setTimeout
226
+
227
+ The `this.$setTimeout()`-method creates a timeout, using a thin wrapper around the `window.setTimeout()` method. The first argument is the `callback` to be executed after the `delay` specified in the second argument. The method returns a `timeout id`, which can be used to mannually clear the timeout.
228
+
229
+ ### $clearTimeout
230
+
231
+ The `this.$clearTimeout()`-method is a thin wrapper around the `window.clearTimeout()`-method. By passing in the `id` of a timeout, it will clear and remove the timeout.
232
+
233
+ ### $clearTimeouts
234
+
235
+ The `this.$clearTimeouts()`-method is a utility method that is used to clear all the timeouts created via the `this.$setTimeout()` method in one go. The method doesn't require to pass any timeout ids. This method is automatically called when destroying a Component, and with that it prevents memory leaks due to dangling timeouts.
236
+
237
+ ```js
238
+ export default Blits.Component('MyComponent', {
239
+ state() {
240
+ timeout: null
241
+ },
242
+ hooks: {
243
+ init() {
244
+ this.timeout = this.$setTimeout(() => {
245
+ console.log('1 minute has passed!')
246
+ }, 60000)
247
+
248
+ this.$setTimeout(() => {
249
+ console.log('10 seconds have passed!')
250
+ }, 10000)
251
+ },
252
+ unfocus() {
253
+ // clear all timeouts when Component is unfocused
254
+ this.$clearTimeouts()
255
+ }
256
+ },
257
+ input: {
258
+ enter() {
259
+ // clear the timeout
260
+ this.$clearTimeout(this.timeout)
261
+ }
262
+ }
263
+ })
264
+ ```
265
+
266
+ ## Intervals
267
+
268
+ Timeouts are a great way to execute recurring logic, such as polling for updates or periodically updating an animation.
269
+
270
+ But similar to timeouts, intervals (more specifically failing to clear them) are a common source of memory leaks - maybe even more than dangling timeouts.
271
+
272
+ For this reason Blits has 3 built-in utility methods to help registering and clearing intervals.
273
+
274
+ ### $setInterval
275
+
276
+ The `this.$setInterval()`-method create an interval, using a thin wrapper around the `window.setInterval()` method. The first argument is the `callback` to be executed after each interval `delay` specified in the second argument. The method returns an `interval id`, which can be used to manually clear the interval.
277
+
278
+ ### $clearInterval
279
+
280
+ The `this.$clearInterval()`-method is a thin wrapper around the `window.clearInterval()`-method. By passing in the `id` of an interval, it will clear and remove the interval.
281
+
282
+
283
+ ### $clearIntervals
284
+
285
+ The `this.$clearIntervals()`-method is a utility method that is used to clear all the intervals created via the `this.$setInterval()` method in one go. The method doesn't require to pass any interval ids. This method is automatically called when destroying a Component, and with that it prevents memory leaks due to dangling intervals.
286
+
287
+ ```js
288
+ export default Blits.Component('MyComponent', {
289
+ state() {
290
+ return {
291
+ interval: null,
292
+ secondCount: 0,
293
+ minuteCount: 0,
294
+ }
295
+ },
296
+ hooks: {
297
+ init() {
298
+ this.interval = this.$setInterval(() => {
299
+ this.secondCount++
300
+ console.log(`${this.secondCount} second(s) have passed`)
301
+ }, 1000)
302
+
303
+ this.$setInterval(() => {
304
+ this.minuteCount++
305
+ console.log(`${this.minuteCount} minute(s) have passed`)
306
+ }, 60000)
307
+ },
308
+ unfocus() {
309
+ // clear all intervals when Component is unfocused
310
+ this.$clearIntervals()
311
+ }
312
+ },
313
+ input: {
314
+ enter() {
315
+ // clear the interval
316
+ this.$clearInterval(this.interval)
317
+ }
318
+ }
319
+ })
320
+ ```