@lightningjs/blits 1.3.0 → 1.4.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 +21 -0
- package/docs/_sidebar.md +3 -0
- package/docs/components/utility_methods.md +280 -0
- package/docs/plugins/global_app_state.md +77 -0
- package/docs/plugins/theme.md +176 -0
- package/docs/sidebar.json +12 -0
- package/package.json +2 -2
- package/src/component/base/events.js +4 -3
- package/src/component/base/index.js +2 -3
- package/src/component/base/methods.js +31 -4
- package/src/component.d.ts +15 -1
- package/src/component.js +25 -12
- package/src/components/Layout.js +1 -1
- package/src/components/Layout.orig.js +1 -1
- package/src/components/RouterView.js +1 -1
- package/src/components/RouterView.orig.js +1 -1
- package/src/engines/L3/element.js +27 -0
- package/src/engines/L3/element.test.js +112 -0
- package/src/engines/L3/launch.js +11 -10
- package/src/lib/codegenerator/generator.js +9 -0
- package/src/lib/eventListeners.js +42 -16
- package/src/lib/eventListeners.test.js +185 -0
- package/src/lib/symbols.js +1 -1
- package/src/plugin.js +9 -5
- package/src/plugins/appstate.js +25 -0
- package/src/plugins/index.js +2 -0
- package/src/{component/base → plugins}/log.js +4 -6
- package/src/plugins/theme.js +88 -0
- package/docs/components/utilities.md +0 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v1.4.0
|
|
4
|
+
|
|
5
|
+
_16 sept 2024_
|
|
6
|
+
|
|
7
|
+
- Added support for `fit` attribute to enable resizeModes for images (cover, contain)
|
|
8
|
+
- Added priority parameter for event emitters + ability to stop propagation
|
|
9
|
+
- Added Global App State plugin
|
|
10
|
+
- Added Theme plugin
|
|
11
|
+
- Standardized built-in utility methods (i.e. focus, select, setTimeout, etc.) to all be prefixed with a `$`.
|
|
12
|
+
- Added support for triggering nested state variables with dot-notation (i.e `foo.bar.bla`)
|
|
13
|
+
- Added support for watching nested state variables (i.e `foo.bar.bla`)
|
|
14
|
+
- Fixed bug with Log plugin not picking up on `debugLevel` setting
|
|
15
|
+
- Fixed bug when using hex-colors in an effect
|
|
16
|
+
- Upgraded to latest version of the renderer (2.1.1)
|
|
17
|
+
|
|
18
|
+
## v1.3.1
|
|
19
|
+
|
|
20
|
+
_27 aug 2024_
|
|
21
|
+
|
|
22
|
+
- Moved `this.$log` functionality to a plugin, which delays the instantation and allows it to pick up launch settings (bug fix)
|
|
23
|
+
|
|
3
24
|
## v1.3.0
|
|
4
25
|
|
|
5
26
|
_19 aug 2024_
|
package/docs/_sidebar.md
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
- [Hooking into lifecycle events](/components/lifecycle_events.md)
|
|
18
18
|
- [Handling User input](/components/user_input.md)
|
|
19
19
|
- [Methods](/components/methods.md)
|
|
20
|
+
- [Utility methods](/components/utility_methods.md)
|
|
20
21
|
- Transitions and Animations
|
|
21
22
|
- [Transitions](/transitions_animations/transitions.md)
|
|
22
23
|
- Built-in components
|
|
@@ -25,3 +26,5 @@
|
|
|
25
26
|
- Plugins
|
|
26
27
|
- [Text-to-Speech / Announcer](/plugins/text-to-speech-announcer.md)
|
|
27
28
|
- [Language](/plugins/language.md)
|
|
29
|
+
- [Theme](/plugins/theme.md)
|
|
30
|
+
- [Global App State](/plugins/global_app_state.md)
|
|
@@ -0,0 +1,280 @@
|
|
|
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
|
+
## Timeouts
|
|
181
|
+
|
|
182
|
+
Setting a timeout is a typical way in JS to execute functionality with a delay. But timeouts can also be a common cause of
|
|
183
|
+
memory leaks.
|
|
184
|
+
|
|
185
|
+
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.
|
|
186
|
+
|
|
187
|
+
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.
|
|
188
|
+
|
|
189
|
+
### $setTimeout
|
|
190
|
+
|
|
191
|
+
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.
|
|
192
|
+
|
|
193
|
+
### $clearTimeout
|
|
194
|
+
|
|
195
|
+
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.
|
|
196
|
+
|
|
197
|
+
### $clearTimeouts
|
|
198
|
+
|
|
199
|
+
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.
|
|
200
|
+
|
|
201
|
+
```js
|
|
202
|
+
export default Blits.Component('MyComponent', {
|
|
203
|
+
state() {
|
|
204
|
+
timeout: null
|
|
205
|
+
},
|
|
206
|
+
hooks: {
|
|
207
|
+
init() {
|
|
208
|
+
this.timeout = this.$setTimeout(() => {
|
|
209
|
+
console.log('1 minute has passed!')
|
|
210
|
+
}, 60000)
|
|
211
|
+
|
|
212
|
+
this.$setTimeout(() => {
|
|
213
|
+
console.log('10 seconds have passed!')
|
|
214
|
+
}, 10000)
|
|
215
|
+
},
|
|
216
|
+
unfocus() {
|
|
217
|
+
// clear all timeouts when Component is unfocused
|
|
218
|
+
this.$clearTimeouts()
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
input: {
|
|
222
|
+
enter() {
|
|
223
|
+
// clear the timeout
|
|
224
|
+
this.$clearTimeout(this.timeout)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
})
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Intervals
|
|
231
|
+
|
|
232
|
+
Timeouts are a great way to execute recurring logic, such as polling for updates or periodically updating an animation.
|
|
233
|
+
|
|
234
|
+
But simular to timeouts, intervals (more specifically failing to clear them) are a common source of memory leaks - maybe even more than dangling timeouts.
|
|
235
|
+
|
|
236
|
+
For this reason Blits has 3 built-in utility methods to help registering and clearing intervals.
|
|
237
|
+
|
|
238
|
+
### $setInterval
|
|
239
|
+
|
|
240
|
+
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 mannually clear the interval.
|
|
241
|
+
|
|
242
|
+
### $clearInterval
|
|
243
|
+
|
|
244
|
+
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.
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
### $clearIntervals
|
|
248
|
+
|
|
249
|
+
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.
|
|
250
|
+
|
|
251
|
+
```js
|
|
252
|
+
export default Blits.Component('MyComponent', {
|
|
253
|
+
state() {
|
|
254
|
+
interval: null,
|
|
255
|
+
secondCount: 0,
|
|
256
|
+
minuteCount: 0,
|
|
257
|
+
},
|
|
258
|
+
hooks: {
|
|
259
|
+
init() {
|
|
260
|
+
this.interval = this.$setInterval(() => {
|
|
261
|
+
console.log(`${secondCount} second(s) have passed`)
|
|
262
|
+
}, 1000)
|
|
263
|
+
|
|
264
|
+
this.$setInterval(() => {
|
|
265
|
+
console.log(`${minuteCount} minute(s) have passed`)
|
|
266
|
+
}, 60000)
|
|
267
|
+
},
|
|
268
|
+
unfocus() {
|
|
269
|
+
// clear all intervals when Component is unfocused
|
|
270
|
+
this.$clearIntervals()
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
input: {
|
|
274
|
+
enter() {
|
|
275
|
+
// clear the interval
|
|
276
|
+
this.$clearInterval(this.interval)
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
})
|
|
280
|
+
```
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Global App State plugin
|
|
2
|
+
|
|
3
|
+
Each Blits Component has its own, internal component state that is reactive and can automatically trigger updates in the template upon changes. The internal state is fully encapsulated, which ensures predictable component state and promotes a pattern of clear separation of concerns.
|
|
4
|
+
|
|
5
|
+
But a Component usually doesn't live on its own, instead it's part of a bigger App. And there may arise a need for components to exchange data - intercomponent communication in other words.
|
|
6
|
+
|
|
7
|
+
Blits offers the option to _emit_ and _listen_ to events, as explained in the section on Blits [utility methods](../components/utility_methods.md). For simple use cases, this is a convenient method to pass data along. But when used extensively, it may be easy to lose track of data flows.
|
|
8
|
+
|
|
9
|
+
For more advanced cases, Blits offers an optional Global App State plugin that will act as a global state store that all components have direct access to. It's a simple implementation, focused on providing a _performant_ and _easy to use_ solution for global state. It's not a full flux / redux style state management solution, as we know from experience that these solutions (while very useful and cool), may lead to performance issues on low-end devices.
|
|
10
|
+
|
|
11
|
+
## Registering the plugin
|
|
12
|
+
|
|
13
|
+
The Global App state plugin is provided in the core Blits package, but as it's an _optional_ plugin it first needs to be registered.
|
|
14
|
+
|
|
15
|
+
The plugin can be imported from Blits and registered in the App's `index.js`, as demonstrated in the example below.
|
|
16
|
+
|
|
17
|
+
Make sure to place the `Blits.Plugin()` method _before_ calling the `Blits.Launch()` method.
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
// index.js
|
|
21
|
+
|
|
22
|
+
import Blits from '@lightningjs/blits'
|
|
23
|
+
// import the app state plugin
|
|
24
|
+
import { appState } from '@lightningjs/blits/plugins'
|
|
25
|
+
|
|
26
|
+
import App from './App.js'
|
|
27
|
+
|
|
28
|
+
// Use the Blits App State plugin
|
|
29
|
+
Blits.Plugin(appState, {
|
|
30
|
+
loggedIn: false,
|
|
31
|
+
user: {
|
|
32
|
+
id: null,
|
|
33
|
+
name: '',
|
|
34
|
+
lastname: ''
|
|
35
|
+
},
|
|
36
|
+
languages: ['en', 'nl', 'pt', 'es'],
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
Blits.Launch(App, 'app', {
|
|
41
|
+
// launch settings
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### State object
|
|
46
|
+
|
|
47
|
+
When registering the Global App State plugin, you pass it a state object as the second argument. This is a plain object literal that will be converted into a _reactive_ object, and is used to keep track the global state variables.
|
|
48
|
+
|
|
49
|
+
The newly created Global App state works exactly the same as an internal Component state. You can read values and you can directly change values. And when you do change a value, it automatically triggers reactive updates. Either via reactive attributes in the template, or in the form of watchers / computed values in the Component logic.
|
|
50
|
+
|
|
51
|
+
### Using global app state in a Component
|
|
52
|
+
|
|
53
|
+
Any variable in the Global App state can be used directly in the template, much like a local Component state. Changing values in the global app state also works exactly the same as updating the internal component state.
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
Blits.Component('MyComponent', {
|
|
57
|
+
template: `
|
|
58
|
+
<Element>
|
|
59
|
+
<Text :content="$$appState.loggedIn ? $$appState.user.name : 'Not logged in'" />
|
|
60
|
+
</Element>
|
|
61
|
+
`,
|
|
62
|
+
input: {
|
|
63
|
+
enter() {
|
|
64
|
+
this.$appState.user.name = 'John'
|
|
65
|
+
this.$appState.loggedIn = true
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
})
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Note that in the template definition, 2 consecutive dollars signs are used (`$$appState`). The first `$`-sign is used
|
|
72
|
+
to refer to the Component scope, as with any other state variable or computed property referenced in the template.
|
|
73
|
+
|
|
74
|
+
The second `$`-sign is needed, since the appState plugin itself is prefixed with a dollar sign, to prevent accidental naming collisions.
|
|
75
|
+
|
|
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
|
+
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Theme plugin
|
|
2
|
+
|
|
3
|
+
Blits comes with a built-in, lightweight *Theme* plugin, that can be used to easily add theming capabilities to an App.
|
|
4
|
+
|
|
5
|
+
The Blits Theme plugin provides the option to do real time theme switching, with inheritance from a base theme for missing values.
|
|
6
|
+
|
|
7
|
+
## Registering the plugin
|
|
8
|
+
|
|
9
|
+
The Theme plugin is part of the core Blits package, but it's an optional plugin that needs to be registered in the App's `index.js`,
|
|
10
|
+
as demonstrated in the example below.
|
|
11
|
+
|
|
12
|
+
Make sure to place the `Blits.Plugin()` method _before_ calling the `Blits.Launch()` method
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
// index.js
|
|
16
|
+
|
|
17
|
+
import Blits from '@lightningjs/blits'
|
|
18
|
+
// import the theme plugin
|
|
19
|
+
import { theme } from '@lightningjs/blits/plugins'
|
|
20
|
+
|
|
21
|
+
import App from './App.js'
|
|
22
|
+
|
|
23
|
+
// Use the Blits Theme plugin
|
|
24
|
+
Blits.Plugin(theme, {
|
|
25
|
+
// theme configuration object
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
Blits.Launch(App, 'app', {
|
|
30
|
+
// launch settings
|
|
31
|
+
})
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Configuration
|
|
35
|
+
|
|
36
|
+
The Theme plugin requires configuration that specifies the theming definition(s).
|
|
37
|
+
|
|
38
|
+
For simple use cases, with one single theme where you just want to abstract and centralize the styling information for an App,
|
|
39
|
+
the configuration object can be a simple object that specifies a theme definition.
|
|
40
|
+
|
|
41
|
+
A theme definition is a completely freeform object literal with key values pairs. It can be organized and constructed however makes most sense for the App. See below an example for inspiration:
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
Blits.Plugin(theme, {
|
|
45
|
+
colors: {
|
|
46
|
+
primary: '#16a34a',
|
|
47
|
+
secondary: '#2563eb',
|
|
48
|
+
highlight: '#ec4899'
|
|
49
|
+
},
|
|
50
|
+
radius: {
|
|
51
|
+
small: 4,
|
|
52
|
+
medium: 8,
|
|
53
|
+
large: 12
|
|
54
|
+
},
|
|
55
|
+
fontSizes: {
|
|
56
|
+
h1: 80,
|
|
57
|
+
h2: 64,
|
|
58
|
+
body: 42
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
> While it's possible to create deeply nested structures in the theme definition, it is recommended to limit the level of nesting for performance reasons.
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
When an App has more than 1 theme, the _advanced configuration_ setup can be used. This configuration object can contain the following 3 keys:
|
|
67
|
+
|
|
68
|
+
- `themes` - an object with multiple theme definitions (required)
|
|
69
|
+
- `base` - the name of the base theme in the `themes`-object, that other themes can inherit from (optional, defaults to `default`)
|
|
70
|
+
- `current` - the name of current theme to be used (optional, defaults to `default`)
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
Blits.Plugin(theme, {
|
|
74
|
+
themes: {
|
|
75
|
+
// Base theme
|
|
76
|
+
base: {
|
|
77
|
+
colors: {
|
|
78
|
+
primary: '#16a34a',
|
|
79
|
+
secondary: '#2563eb',
|
|
80
|
+
highlight: '#ec4899'
|
|
81
|
+
},
|
|
82
|
+
radius: {
|
|
83
|
+
small: 4,
|
|
84
|
+
medium: 8,
|
|
85
|
+
large: 12
|
|
86
|
+
},
|
|
87
|
+
fontSizes: {
|
|
88
|
+
h1: 80,
|
|
89
|
+
h2: 64,
|
|
90
|
+
body: 42
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
// Dark mode theme
|
|
94
|
+
dark: {
|
|
95
|
+
colors: {
|
|
96
|
+
primary: '#042f2e',
|
|
97
|
+
secondary: '#082f49'
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
// Theme with larger font sizes
|
|
101
|
+
large: {
|
|
102
|
+
fontSizes: {
|
|
103
|
+
h1: 160,
|
|
104
|
+
h2: 124,
|
|
105
|
+
body: 84
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
base: 'base',
|
|
110
|
+
current: 'large'
|
|
111
|
+
})
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
In the definition above we've specified 3 different themes: `base`, `dark` and `large`. The dark and large theme are not complete definitions,
|
|
115
|
+
which mean that they will inherit missing values from the base theme.
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
## Getting theme values
|
|
119
|
+
|
|
120
|
+
With the configuration out of the way, let's look at how to use the specified theme values in an App.
|
|
121
|
+
|
|
122
|
+
The Theme plugin will be available in every component under the `this.$theme`-namespace. It exposes a `get()` method than can be used to
|
|
123
|
+
retrieve values from the currently active theme definition.
|
|
124
|
+
|
|
125
|
+
The `this.$theme.get()`-method accepts a `key`-argument, using _dot-notation_ (i.e. `fonts.large.lineheight`) to reference items in the theme definition. An optional second argument can be provided as a fallback value, in case the key is missing in the theme (as well as in the base theme).
|
|
126
|
+
|
|
127
|
+
Inside of the template, the `this.$theme.get()`-method can be referenced as `$$theme.get()`. The first `$`-sign is used
|
|
128
|
+
to refer to the Component scope (as with any other state variable or computed property in Blits) and the second `$`-sign is the prefix of
|
|
129
|
+
the Theme plugin itself.
|
|
130
|
+
|
|
131
|
+
```js
|
|
132
|
+
Blits.Component('MyComponent', {
|
|
133
|
+
template: `
|
|
134
|
+
<Element>
|
|
135
|
+
<Text content="Hello world"
|
|
136
|
+
:color="$$theme.get('colors.primary', 'blue')"
|
|
137
|
+
:size="$$theme.get('fontSizes.body')"
|
|
138
|
+
/>
|
|
139
|
+
</Element>
|
|
140
|
+
`,
|
|
141
|
+
computed: {
|
|
142
|
+
highlight() {
|
|
143
|
+
return this.$theme.get('colors.highlight')
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
})
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Changing themes
|
|
150
|
+
|
|
151
|
+
In case you have specified multiple themes, using the advanced configuration, it is possible to do real-time theme switching.
|
|
152
|
+
|
|
153
|
+
Besides the `get()` method, the Theme plugin also exposes a `set()`-method, to change the currently active theme. The `this.$theme.set()`-method accepts the name of the theme as its first argument.
|
|
154
|
+
|
|
155
|
+
```js
|
|
156
|
+
Blits.Component('MyComponent', {
|
|
157
|
+
template: `
|
|
158
|
+
<Element>
|
|
159
|
+
<Text content="Hello world"
|
|
160
|
+
:color="$$theme.get('colors.primary', 'blue')"
|
|
161
|
+
:size="$$theme.get('fontSizes.body')"
|
|
162
|
+
/>
|
|
163
|
+
</Element>
|
|
164
|
+
`,
|
|
165
|
+
input: {
|
|
166
|
+
up() {
|
|
167
|
+
this.$theme.set('dark')
|
|
168
|
+
},
|
|
169
|
+
down() {
|
|
170
|
+
this.$theme.set('default')
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
})
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
All attributes referencing the Theme plugin that are marked as _reactive_ (i.e. those prefixed with a `:`), will update instantly when changing the theme.
|
package/docs/sidebar.json
CHANGED
|
@@ -75,6 +75,10 @@
|
|
|
75
75
|
{
|
|
76
76
|
"text": "Methods",
|
|
77
77
|
"link": "/components/methods"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"text": "Utility methods",
|
|
81
|
+
"link": "/components/utility_methods"
|
|
78
82
|
}
|
|
79
83
|
]
|
|
80
84
|
},
|
|
@@ -110,6 +114,14 @@
|
|
|
110
114
|
{
|
|
111
115
|
"text": "Language",
|
|
112
116
|
"link": "/plugins/language"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"text": "Theme",
|
|
120
|
+
"link": "/plugins/theme"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"text": "Global App State",
|
|
124
|
+
"link": "/plugins/global_app_state"
|
|
113
125
|
}
|
|
114
126
|
]
|
|
115
127
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightningjs/blits",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Blits: The Lightning 3 App Development Framework",
|
|
5
5
|
"bin": "bin/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@lightningjs/msdf-generator": "^1.0.2",
|
|
50
|
-
"@lightningjs/renderer": "^1.
|
|
50
|
+
"@lightningjs/renderer": "^2.1.2",
|
|
51
51
|
"execa": "^8.0.1",
|
|
52
52
|
"kolorist": "^1.8.0",
|
|
53
53
|
"ora": "^7.0.1",
|
|
@@ -20,15 +20,16 @@ import eventListeners from '../../lib/eventListeners.js'
|
|
|
20
20
|
export default {
|
|
21
21
|
$emit: {
|
|
22
22
|
value: function (event, params) {
|
|
23
|
-
|
|
23
|
+
// returning if all listeners executed
|
|
24
|
+
return eventListeners.executeListeners(event, params)
|
|
24
25
|
},
|
|
25
26
|
writable: false,
|
|
26
27
|
enumerable: true,
|
|
27
28
|
configurable: false,
|
|
28
29
|
},
|
|
29
30
|
$listen: {
|
|
30
|
-
value: function (event, callback) {
|
|
31
|
-
eventListeners.registerListener(this, event, callback)
|
|
31
|
+
value: function (event, callback, priority = 0) {
|
|
32
|
+
eventListeners.registerListener(this, event, callback, priority)
|
|
32
33
|
},
|
|
33
34
|
writable: false,
|
|
34
35
|
enumerable: true,
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
import methods from './methods.js'
|
|
19
19
|
import scheduling from './timeouts_intervals.js'
|
|
20
20
|
import events from './events.js'
|
|
21
|
-
import log from './log.js'
|
|
22
21
|
import router from './router.js'
|
|
23
22
|
import announcer from './announcer.js'
|
|
24
23
|
import utils from './utils.js'
|
|
@@ -26,7 +25,7 @@ import symbols from '../../lib/symbols.js'
|
|
|
26
25
|
|
|
27
26
|
export default Object.defineProperties(
|
|
28
27
|
{
|
|
29
|
-
[symbols['
|
|
28
|
+
[symbols['launched']]: false,
|
|
30
29
|
},
|
|
31
|
-
{ ...methods, ...scheduling, ...events, ...
|
|
30
|
+
{ ...methods, ...scheduling, ...events, ...router, ...announcer, ...utils }
|
|
32
31
|
)
|