@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.
- package/.editorconfig +7 -7
- package/.eslintignore +1 -1
- package/.eslintrc.cjs +65 -65
- package/.github/workflows/comment-lint-results.yml +124 -124
- package/.github/workflows/comment-test-results.yml +108 -108
- package/.github/workflows/deploy-docs.yml +27 -27
- package/.github/workflows/run-blits-tests.yml +141 -141
- package/.github/workflows/run-lint.yml +127 -127
- package/.husky/pre-commit +1 -1
- package/.vscode/extensions.json +7 -7
- package/.vscode/settings.json +9 -9
- package/CHANGELOG.md +1019 -1005
- package/COPYING +1 -0
- package/LICENSE +202 -202
- package/NOTICE +12 -12
- package/README.md +33 -33
- package/bin/index.js +8 -8
- package/blits.d.ts +6 -6
- package/docs/README.md +19 -19
- package/docs/_sidebar.md +37 -37
- package/docs/built-in/directives.md +50 -50
- package/docs/built-in/for-loop.md +203 -203
- package/docs/built-in/layout.md +168 -168
- package/docs/components/component_state.md +90 -90
- package/docs/components/computed_properties.md +45 -45
- package/docs/components/lifecycle_events.md +42 -42
- package/docs/components/methods.md +45 -45
- package/docs/components/props.md +56 -56
- package/docs/components/user_input.md +186 -186
- package/docs/components/utility_methods.md +320 -320
- package/docs/components/watchers.md +34 -34
- package/docs/essentials/application_root.md +19 -19
- package/docs/essentials/components.md +68 -68
- package/docs/essentials/displaying_images.md +67 -67
- package/docs/essentials/displaying_text.md +135 -135
- package/docs/essentials/element_attributes.md +195 -195
- package/docs/essentials/settings.md +117 -117
- package/docs/essentials/template_syntax.md +83 -83
- package/docs/getting_started/file_structure.md +58 -58
- package/docs/getting_started/getting_started.md +45 -45
- package/docs/getting_started/intro.md +20 -20
- package/docs/index.html +47 -47
- package/docs/performance/lazy-loading.md +161 -161
- package/docs/plugins/global_app_state.md +98 -98
- package/docs/plugins/language.md +172 -172
- package/docs/plugins/text-to-speech-announcer.md +119 -119
- package/docs/plugins/theme.md +176 -176
- package/docs/router/basics.md +175 -175
- package/docs/router/hooks.md +70 -70
- package/docs/router/transitions.md +146 -146
- package/docs/sidebar.json +171 -171
- package/docs/transitions_animations/transitions.md +150 -150
- package/index.d.ts +1099 -1099
- package/index.js +28 -28
- package/jsconfig.json +12 -12
- package/package.json +56 -56
- package/public/assets/fps_sprite.base64.js +1 -1
- package/scripts/postpublish.js +59 -59
- package/scripts/prepublishOnly.js +84 -84
- package/scripts/prepush.js +78 -78
- package/src/announcer/announcer.js +162 -162
- package/src/announcer/speechSynthesis.js +107 -107
- package/src/application.js +100 -100
- package/src/component/base/announcer.js +27 -27
- package/src/component/base/events.js +69 -69
- package/src/component/base/index.js +33 -33
- package/src/component/base/methods.js +244 -244
- package/src/component/base/router.js +41 -41
- package/src/component/base/timeouts_intervals.js +99 -99
- package/src/component/base/utils.js +72 -72
- package/src/component/setup/computed.js +48 -48
- package/src/component/setup/index.js +62 -62
- package/src/component/setup/input.js +30 -30
- package/src/component/setup/methods.js +37 -37
- package/src/component/setup/props.js +62 -62
- package/src/component/setup/props.test.js +238 -238
- package/src/component/setup/routes.js +42 -42
- package/src/component/setup/state.js +59 -59
- package/src/component/setup/watch.js +35 -35
- package/src/component.js +437 -437
- package/src/component.test.js +388 -388
- package/src/components/Circle.js +73 -73
- package/src/components/Circle.orig.js +31 -31
- package/src/components/FPScounter.js +1510 -1510
- package/src/components/FPScounter.orig.js +121 -121
- package/src/components/RouterView.js +81 -81
- package/src/components/RouterView.orig.js +54 -54
- package/src/components/Sprite.js +94 -94
- package/src/components/Sprite.orig.js +58 -58
- package/src/components/index.js +28 -28
- package/src/components/index.orig.js +28 -28
- package/src/constants.js +21 -21
- package/src/engine.js +1 -1
- package/src/engines/L3/element.js +744 -744
- package/src/engines/L3/element.test.js +579 -579
- package/src/engines/L3/fontLoader.js +38 -38
- package/src/engines/L3/index.js +7 -7
- package/src/engines/L3/launch.js +123 -123
- package/src/engines/L3/shaderLoader.js +12 -12
- package/src/engines/index.js +1 -1
- package/src/focus.js +90 -90
- package/src/helpers/deepEqualArray.js +50 -50
- package/src/helpers/deepEqualArray.test.js +127 -127
- package/src/helpers/fetchJson.js +34 -34
- package/src/helpers/sequence.js +26 -26
- package/src/launch.js +123 -123
- package/src/lib/blitsfileconverter/blitsfileconverter.js +52 -52
- package/src/lib/codegenerator/generator.js +788 -788
- package/src/lib/codegenerator/generator.test.js +3801 -3801
- package/src/lib/colors/colors.js +92 -92
- package/src/lib/colors/colors.test.js +290 -290
- package/src/lib/colors/htmlColors.js +168 -168
- package/src/lib/componentId.js +49 -49
- package/src/lib/componentId.test.js +84 -84
- package/src/lib/eventListeners.js +123 -123
- package/src/lib/eventListeners.test.js +236 -236
- package/src/lib/hooks.js +59 -59
- package/src/lib/lifecycle.js +85 -85
- package/src/lib/log.js +117 -117
- package/src/lib/precompiler/precompiler.js +73 -73
- package/src/lib/reactivity/effect.js +93 -93
- package/src/lib/reactivity/effect.test.js +35 -35
- package/src/lib/reactivity/reactive.js +208 -208
- package/src/lib/reactivity/reactive.test.js +27 -27
- package/src/lib/reactivityguard/computedprops.js +218 -218
- package/src/lib/symbols.js +108 -108
- package/src/lib/templateparser/parser.js +386 -386
- package/src/lib/templateparser/parser.test.js +1411 -1411
- package/src/lib/utils.js +27 -27
- package/src/plugin.js +27 -27
- package/src/plugins/appstate.js +26 -26
- package/src/plugins/index.js +20 -20
- package/src/plugins/language.js +152 -152
- package/src/plugins/log.js +25 -25
- package/src/plugins/theme.js +94 -94
- package/src/router/router.js +514 -514
- package/src/router/router.test.js +473 -473
- package/src/router/transitions/fadeIn.js +27 -27
- package/src/router/transitions/fadeInOut.js +33 -33
- package/src/router/transitions/index.js +21 -21
- package/src/router/transitions/slideIn.js +28 -28
- package/src/router/transitions/slideInOut.js +33 -33
- package/src/settings.js +40 -40
- package/vite/blitsFileConverter.js +67 -67
- package/vite/index.js +28 -28
- package/vite/msdfGenerator.js +269 -269
- package/vite/preCompiler.js +42 -42
- package/vite/reactivityGuard.js +45 -45
- package/vscode/data/template-attributes.json +589 -589
package/docs/built-in/layout.md
CHANGED
|
@@ -1,168 +1,168 @@
|
|
|
1
|
-
# Layout
|
|
2
|
-
|
|
3
|
-
A big portion of building a pixel-perfect App is all about correctly positioning Elements and Components on the screen. In Lightning and Blits, _absolute_ positions are used. This fits the whole concept of building a TV app where the viewport dimensions are known and fixed. Absolute positioning (versus spacing Elements relative to each other) is also more performant.
|
|
4
|
-
|
|
5
|
-
Imagine a complex design with several nested layers. Relying solely on _relative_ positioning and elements automatically floating as space is available may lead to heavy and recursive calculations. Then throw some dynamically changing dimensions and animations in the mix as well, and your App may be starting to noticeably drop frames due to expensive re-calculations.
|
|
6
|
-
|
|
7
|
-
That being said, there are often cases where it becomes very cumbersome to calculate all the positions manually. Especially when dimensions are not known in advance and elements may load asynchronously.
|
|
8
|
-
|
|
9
|
-
If you come from a CSS background, you may be tempted to say: "_this is why we have Flexbox!_". And while Flexbox is great and versatile in CSS and running on a powerful device, a full flexbox implementation does come at a cost and the risk of slowing down your App, when applied extensively or incorrectly.
|
|
10
|
-
|
|
11
|
-
In order to address the core problem of "_how do I easily position a bunch of Elements and Components without writing a lot of code with `@loaded` event handlers_", Blits offers the built-in `<Layout>` component - a basic and performant solution that automatically puts your Elements in the right place.
|
|
12
|
-
|
|
13
|
-
## How to use
|
|
14
|
-
|
|
15
|
-
Since the Layout component is a built-in component, there is no need to register it separately. You can use the `<Layout>` tag anywhere in your templates, just as you would use `<Element>` and `<Text>`.
|
|
16
|
-
|
|
17
|
-
The Layout component is a wrapper component that encloses a number of children. All direct children wrapped in a `<Layout>` tag are automatically positioned in order, one next to the other.
|
|
18
|
-
|
|
19
|
-
Whenever dimensions of a child change, the positioning of the next children is automatically recalculated and reapplied.
|
|
20
|
-
|
|
21
|
-
```xml
|
|
22
|
-
<Layout>
|
|
23
|
-
<Element w="40" h="40" color="red" />
|
|
24
|
-
<Element w="80" h="40" color="blue" />
|
|
25
|
-
<Text>Hello world</Text>
|
|
26
|
-
<Element w="40" h="40" color="green" />
|
|
27
|
-
</Layout>
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
### Horizontal and vertical layout
|
|
31
|
-
|
|
32
|
-
By default, the Layout component lays out its contents _horizontally_. The Layout component accepts a `direction` attribute that allows you to control the direction.
|
|
33
|
-
|
|
34
|
-
In order to align vertically, use `<Layout direction="vertical"></Layout>`. And use `<Layout direction="horizontal"></Layout>` to explicitly apply the default horizontal layout.
|
|
35
|
-
|
|
36
|
-
### Spacing between children
|
|
37
|
-
|
|
38
|
-
By default, the Layout-component places each Element directly besides (or below) the previous one. By adding the `gap`-attribute, you can control how much space will be added between each Element or Component. The `gap`-attribute accepts a number in pixels.
|
|
39
|
-
|
|
40
|
-
### Aligning items
|
|
41
|
-
|
|
42
|
-
The layout component positions its children based on the provided direction (`horizontal` or `vertical`). With the `align-items`-attribute, you can specify how to align the children on the opposite axis:
|
|
43
|
-
|
|
44
|
-
- `start` (the default value) - aligns the children at the _top_ for horizontal layouts and on the _left_ for vertical layouts
|
|
45
|
-
- `center` - align the children in the center
|
|
46
|
-
- `end` - aligns the children in the _bottom_ for horizontal layouts, and on the _right_ for vertical layouts
|
|
47
|
-
|
|
48
|
-
### Dynamic dimensions
|
|
49
|
-
|
|
50
|
-
For the Layout component to work properly, all direct children need to have dimensions (i.e., `w` and `h` attributes). The exception here being Text elements.
|
|
51
|
-
|
|
52
|
-
Due to the asynchronous nature of rendering text, the final text dimensions are not known beforehand. The width and height of Text elements are automatically set when the text has been loaded. This fires a `@loaded` event that you can tap into as described [here](../essentials/displaying_text#text-dimensions).
|
|
53
|
-
|
|
54
|
-
The Layout component also uses this event to execute its calculation and properly position the text and other elements around it.
|
|
55
|
-
|
|
56
|
-
When the children of the Layout-component have _reactive_ dimensions (i.e., `<Element :w="$mywidth" :h="$myheight" />`), the Layout component ensures that all child elements are properly repositioned whenever a dimension changes.
|
|
57
|
-
|
|
58
|
-
### Components inside a Layout
|
|
59
|
-
|
|
60
|
-
It is also possible to place Components inside of a Layout, but there is a small _gotcha_ there. By default a Component does not have any dimensions - it has a width and height of `0`, regardless of the contents of the Component. Normally, when using absolute positioning, this isn't a problem. But in the context of a Layout, each child needs to have dimensions.
|
|
61
|
-
|
|
62
|
-
If the Component has fixed dimensions, you can just add a `w` and a `h` attribute to the Component tag (i.e. `<MyButton w="100" h="40" />`). This is the most performant way to supply dimensions to a Component and should be used whenever possible.
|
|
63
|
-
|
|
64
|
-
If the Component has dynamic dimensions that are not known upfront, you can dynamically update the dimensions from inside the Component by calling the `this.$size()`-method. This method accepts an object as its argument with a `w` property for setting the _width_ and a `h` property for setting the _height_.
|
|
65
|
-
|
|
66
|
-
```js
|
|
67
|
-
export default Blits.Component('MyButton', {
|
|
68
|
-
template: ``,
|
|
69
|
-
props: ['type'],
|
|
70
|
-
hooks: {
|
|
71
|
-
ready() {
|
|
72
|
-
if(this.type === 'large') {
|
|
73
|
-
this.$size({
|
|
74
|
-
w: 200,
|
|
75
|
-
h: 80
|
|
76
|
-
})
|
|
77
|
-
} else {
|
|
78
|
-
this.$size({
|
|
79
|
-
w: 100,
|
|
80
|
-
h: 40
|
|
81
|
-
})
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
})
|
|
87
|
-
```
|
|
88
|
-
At this moment, Blits does not support automatically growing a Component's dimensions based on its contents due to the performance impact of this functionality.
|
|
89
|
-
|
|
90
|
-
### Nesting Layouts
|
|
91
|
-
|
|
92
|
-
It's also possible to nest layouts. Simply place a new `<Layout>`-tag, with it's own children in between the children of another Layout-component. The Layout component itself will grow automatically with the dimensions of its children. In other words, it's not required to specify a width (`w`) or height (`h`) on the `<Layout>` tag itself.
|
|
93
|
-
|
|
94
|
-
And of course you can nest _vertical_ Layouts inside a _horizontal_ one - and vice versa.
|
|
95
|
-
|
|
96
|
-
### Padding
|
|
97
|
-
|
|
98
|
-
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.
|
|
99
|
-
|
|
100
|
-
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.
|
|
101
|
-
|
|
102
|
-
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`.
|
|
103
|
-
|
|
104
|
-
```xml
|
|
105
|
-
<Layout color="silver" padding="10" >
|
|
106
|
-
<Element w="40" h="40" color="red" />
|
|
107
|
-
<Element w="80" h="40" color="blue" />
|
|
108
|
-
<Element w="40" h="40" color="green" />
|
|
109
|
-
</Layout>
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
```xml
|
|
113
|
-
<Layout color="silver" padding="{x: 20, top: 30, bottom: 10}">
|
|
114
|
-
<Element w="40" h="40" color="red" />
|
|
115
|
-
<Element w="80" h="40" color="blue" />
|
|
116
|
-
<Element w="40" h="40" color="green" />
|
|
117
|
-
</Layout>
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### Transitioning layouts
|
|
121
|
-
|
|
122
|
-
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.
|
|
123
|
-
|
|
124
|
-
### Updated event
|
|
125
|
-
|
|
126
|
-
The `<Layout>`-tag automatically updates its dimensions based on the dimensions of its children. After each update in the children, an `updated`-event is emitted on the `<Layout>`-tag. It will receive the current dimensions of the layout.
|
|
127
|
-
|
|
128
|
-
You can tap into this event by adding an `@updated`-attribute to the `<Layout />`-tag and refer to a method in your Component logic.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
```js
|
|
132
|
-
export default Blits.Component('LayoutUpdate', {
|
|
133
|
-
template: `
|
|
134
|
-
<Element>
|
|
135
|
-
<Layout @updated="$layoutUpdate">
|
|
136
|
-
<Element :w="$width" h="40" color="red" />
|
|
137
|
-
<Element :w="$width" h="40" color="blue" />
|
|
138
|
-
<Element :w="$width" h="40" color="green" />
|
|
139
|
-
</Layout>
|
|
140
|
-
</Element>
|
|
141
|
-
`,
|
|
142
|
-
methods: {
|
|
143
|
-
layoutUpdate(dimensions, el) {
|
|
144
|
-
console.log(`Layout (${el.nodeId}) dimensions updated! Width: ${dimensions.w}, Height: ${dimensions.h}`)
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
})
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
> 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.
|
|
151
|
-
|
|
152
|
-
## Performance
|
|
153
|
-
|
|
154
|
-
The Layout component is a very useful utility that will make it a lot easier to put together complex layouts. It will reduce the code required, it means less hardcoding of values and removes the need to manually wire up `@loaded` events when working with dynamic / asynchronous elements.
|
|
155
|
-
|
|
156
|
-
We have done our best to make the Component as performant as possible by deliberately keeping it simple and straightforward. However, it is important to understand that the Layout component does come at a cost. Depending on the use case these costs may be negligible, but there are cases where the Layout component might prove to be a bottleneck.
|
|
157
|
-
|
|
158
|
-
The performance costs may always be there to some degree. Whether it's the Layout component that does the complex calculations, or you do it in your custom code. So, please feel encouraged to use the `<Layout>` tag!
|
|
159
|
-
|
|
160
|
-
Some pointers to keep in mind, though:
|
|
161
|
-
|
|
162
|
-
- Every change in the dimensions of a child, triggers the Layout component to recalculate and reposition. If your child elements change frequently, the Layout component may have a performance impact.
|
|
163
|
-
|
|
164
|
-
- The Layout component support transitions, but beware that when transitioning child dimensions a recalculation happens every frame tick. If you see frame-drops in your App, you may want to see if the `<Layout>`-tag has an impact on this.
|
|
165
|
-
|
|
166
|
-
- Be mindful to not apply unnecessary deep nesting of Layout tags. Each change in a deep child, will trigger recalculations up the tree of Layout tags.
|
|
167
|
-
|
|
168
|
-
- When using the `<Layout>`-tag with a for-loop, it may be more performant to use the `$index` of the for-loop and manually position the elements yourself (i.e. `<Element :for="(item, index) in $items" :$x="$item * 200" w="150" h="150" />`). Especially if the dimensions are known beforehand and possibly are the same for each child.
|
|
1
|
+
# Layout
|
|
2
|
+
|
|
3
|
+
A big portion of building a pixel-perfect App is all about correctly positioning Elements and Components on the screen. In Lightning and Blits, _absolute_ positions are used. This fits the whole concept of building a TV app where the viewport dimensions are known and fixed. Absolute positioning (versus spacing Elements relative to each other) is also more performant.
|
|
4
|
+
|
|
5
|
+
Imagine a complex design with several nested layers. Relying solely on _relative_ positioning and elements automatically floating as space is available may lead to heavy and recursive calculations. Then throw some dynamically changing dimensions and animations in the mix as well, and your App may be starting to noticeably drop frames due to expensive re-calculations.
|
|
6
|
+
|
|
7
|
+
That being said, there are often cases where it becomes very cumbersome to calculate all the positions manually. Especially when dimensions are not known in advance and elements may load asynchronously.
|
|
8
|
+
|
|
9
|
+
If you come from a CSS background, you may be tempted to say: "_this is why we have Flexbox!_". And while Flexbox is great and versatile in CSS and running on a powerful device, a full flexbox implementation does come at a cost and the risk of slowing down your App, when applied extensively or incorrectly.
|
|
10
|
+
|
|
11
|
+
In order to address the core problem of "_how do I easily position a bunch of Elements and Components without writing a lot of code with `@loaded` event handlers_", Blits offers the built-in `<Layout>` component - a basic and performant solution that automatically puts your Elements in the right place.
|
|
12
|
+
|
|
13
|
+
## How to use
|
|
14
|
+
|
|
15
|
+
Since the Layout component is a built-in component, there is no need to register it separately. You can use the `<Layout>` tag anywhere in your templates, just as you would use `<Element>` and `<Text>`.
|
|
16
|
+
|
|
17
|
+
The Layout component is a wrapper component that encloses a number of children. All direct children wrapped in a `<Layout>` tag are automatically positioned in order, one next to the other.
|
|
18
|
+
|
|
19
|
+
Whenever dimensions of a child change, the positioning of the next children is automatically recalculated and reapplied.
|
|
20
|
+
|
|
21
|
+
```xml
|
|
22
|
+
<Layout>
|
|
23
|
+
<Element w="40" h="40" color="red" />
|
|
24
|
+
<Element w="80" h="40" color="blue" />
|
|
25
|
+
<Text>Hello world</Text>
|
|
26
|
+
<Element w="40" h="40" color="green" />
|
|
27
|
+
</Layout>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Horizontal and vertical layout
|
|
31
|
+
|
|
32
|
+
By default, the Layout component lays out its contents _horizontally_. The Layout component accepts a `direction` attribute that allows you to control the direction.
|
|
33
|
+
|
|
34
|
+
In order to align vertically, use `<Layout direction="vertical"></Layout>`. And use `<Layout direction="horizontal"></Layout>` to explicitly apply the default horizontal layout.
|
|
35
|
+
|
|
36
|
+
### Spacing between children
|
|
37
|
+
|
|
38
|
+
By default, the Layout-component places each Element directly besides (or below) the previous one. By adding the `gap`-attribute, you can control how much space will be added between each Element or Component. The `gap`-attribute accepts a number in pixels.
|
|
39
|
+
|
|
40
|
+
### Aligning items
|
|
41
|
+
|
|
42
|
+
The layout component positions its children based on the provided direction (`horizontal` or `vertical`). With the `align-items`-attribute, you can specify how to align the children on the opposite axis:
|
|
43
|
+
|
|
44
|
+
- `start` (the default value) - aligns the children at the _top_ for horizontal layouts and on the _left_ for vertical layouts
|
|
45
|
+
- `center` - align the children in the center
|
|
46
|
+
- `end` - aligns the children in the _bottom_ for horizontal layouts, and on the _right_ for vertical layouts
|
|
47
|
+
|
|
48
|
+
### Dynamic dimensions
|
|
49
|
+
|
|
50
|
+
For the Layout component to work properly, all direct children need to have dimensions (i.e., `w` and `h` attributes). The exception here being Text elements.
|
|
51
|
+
|
|
52
|
+
Due to the asynchronous nature of rendering text, the final text dimensions are not known beforehand. The width and height of Text elements are automatically set when the text has been loaded. This fires a `@loaded` event that you can tap into as described [here](../essentials/displaying_text#text-dimensions).
|
|
53
|
+
|
|
54
|
+
The Layout component also uses this event to execute its calculation and properly position the text and other elements around it.
|
|
55
|
+
|
|
56
|
+
When the children of the Layout-component have _reactive_ dimensions (i.e., `<Element :w="$mywidth" :h="$myheight" />`), the Layout component ensures that all child elements are properly repositioned whenever a dimension changes.
|
|
57
|
+
|
|
58
|
+
### Components inside a Layout
|
|
59
|
+
|
|
60
|
+
It is also possible to place Components inside of a Layout, but there is a small _gotcha_ there. By default a Component does not have any dimensions - it has a width and height of `0`, regardless of the contents of the Component. Normally, when using absolute positioning, this isn't a problem. But in the context of a Layout, each child needs to have dimensions.
|
|
61
|
+
|
|
62
|
+
If the Component has fixed dimensions, you can just add a `w` and a `h` attribute to the Component tag (i.e. `<MyButton w="100" h="40" />`). This is the most performant way to supply dimensions to a Component and should be used whenever possible.
|
|
63
|
+
|
|
64
|
+
If the Component has dynamic dimensions that are not known upfront, you can dynamically update the dimensions from inside the Component by calling the `this.$size()`-method. This method accepts an object as its argument with a `w` property for setting the _width_ and a `h` property for setting the _height_.
|
|
65
|
+
|
|
66
|
+
```js
|
|
67
|
+
export default Blits.Component('MyButton', {
|
|
68
|
+
template: ``,
|
|
69
|
+
props: ['type'],
|
|
70
|
+
hooks: {
|
|
71
|
+
ready() {
|
|
72
|
+
if(this.type === 'large') {
|
|
73
|
+
this.$size({
|
|
74
|
+
w: 200,
|
|
75
|
+
h: 80
|
|
76
|
+
})
|
|
77
|
+
} else {
|
|
78
|
+
this.$size({
|
|
79
|
+
w: 100,
|
|
80
|
+
h: 40
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
```
|
|
88
|
+
At this moment, Blits does not support automatically growing a Component's dimensions based on its contents due to the performance impact of this functionality.
|
|
89
|
+
|
|
90
|
+
### Nesting Layouts
|
|
91
|
+
|
|
92
|
+
It's also possible to nest layouts. Simply place a new `<Layout>`-tag, with it's own children in between the children of another Layout-component. The Layout component itself will grow automatically with the dimensions of its children. In other words, it's not required to specify a width (`w`) or height (`h`) on the `<Layout>` tag itself.
|
|
93
|
+
|
|
94
|
+
And of course you can nest _vertical_ Layouts inside a _horizontal_ one - and vice versa.
|
|
95
|
+
|
|
96
|
+
### Padding
|
|
97
|
+
|
|
98
|
+
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.
|
|
99
|
+
|
|
100
|
+
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.
|
|
101
|
+
|
|
102
|
+
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`.
|
|
103
|
+
|
|
104
|
+
```xml
|
|
105
|
+
<Layout color="silver" padding="10" >
|
|
106
|
+
<Element w="40" h="40" color="red" />
|
|
107
|
+
<Element w="80" h="40" color="blue" />
|
|
108
|
+
<Element w="40" h="40" color="green" />
|
|
109
|
+
</Layout>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```xml
|
|
113
|
+
<Layout color="silver" padding="{x: 20, top: 30, bottom: 10}">
|
|
114
|
+
<Element w="40" h="40" color="red" />
|
|
115
|
+
<Element w="80" h="40" color="blue" />
|
|
116
|
+
<Element w="40" h="40" color="green" />
|
|
117
|
+
</Layout>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Transitioning layouts
|
|
121
|
+
|
|
122
|
+
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.
|
|
123
|
+
|
|
124
|
+
### Updated event
|
|
125
|
+
|
|
126
|
+
The `<Layout>`-tag automatically updates its dimensions based on the dimensions of its children. After each update in the children, an `updated`-event is emitted on the `<Layout>`-tag. It will receive the current dimensions of the layout.
|
|
127
|
+
|
|
128
|
+
You can tap into this event by adding an `@updated`-attribute to the `<Layout />`-tag and refer to a method in your Component logic.
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
```js
|
|
132
|
+
export default Blits.Component('LayoutUpdate', {
|
|
133
|
+
template: `
|
|
134
|
+
<Element>
|
|
135
|
+
<Layout @updated="$layoutUpdate">
|
|
136
|
+
<Element :w="$width" h="40" color="red" />
|
|
137
|
+
<Element :w="$width" h="40" color="blue" />
|
|
138
|
+
<Element :w="$width" h="40" color="green" />
|
|
139
|
+
</Layout>
|
|
140
|
+
</Element>
|
|
141
|
+
`,
|
|
142
|
+
methods: {
|
|
143
|
+
layoutUpdate(dimensions, el) {
|
|
144
|
+
console.log(`Layout (${el.nodeId}) dimensions updated! Width: ${dimensions.w}, Height: ${dimensions.h}`)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
> 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.
|
|
151
|
+
|
|
152
|
+
## Performance
|
|
153
|
+
|
|
154
|
+
The Layout component is a very useful utility that will make it a lot easier to put together complex layouts. It will reduce the code required, it means less hardcoding of values and removes the need to manually wire up `@loaded` events when working with dynamic / asynchronous elements.
|
|
155
|
+
|
|
156
|
+
We have done our best to make the Component as performant as possible by deliberately keeping it simple and straightforward. However, it is important to understand that the Layout component does come at a cost. Depending on the use case these costs may be negligible, but there are cases where the Layout component might prove to be a bottleneck.
|
|
157
|
+
|
|
158
|
+
The performance costs may always be there to some degree. Whether it's the Layout component that does the complex calculations, or you do it in your custom code. So, please feel encouraged to use the `<Layout>` tag!
|
|
159
|
+
|
|
160
|
+
Some pointers to keep in mind, though:
|
|
161
|
+
|
|
162
|
+
- Every change in the dimensions of a child, triggers the Layout component to recalculate and reposition. If your child elements change frequently, the Layout component may have a performance impact.
|
|
163
|
+
|
|
164
|
+
- The Layout component support transitions, but beware that when transitioning child dimensions a recalculation happens every frame tick. If you see frame-drops in your App, you may want to see if the `<Layout>`-tag has an impact on this.
|
|
165
|
+
|
|
166
|
+
- Be mindful to not apply unnecessary deep nesting of Layout tags. Each change in a deep child, will trigger recalculations up the tree of Layout tags.
|
|
167
|
+
|
|
168
|
+
- When using the `<Layout>`-tag with a for-loop, it may be more performant to use the `$index` of the for-loop and manually position the elements yourself (i.e. `<Element :for="(item, index) in $items" :$x="$item * 200" w="150" h="150" />`). Especially if the dimensions are known beforehand and possibly are the same for each child.
|
|
@@ -1,90 +1,90 @@
|
|
|
1
|
-
# Component State
|
|
2
|
-
|
|
3
|
-
In Blits, a component can be as simple as a template with fixed, hardcoded values. However, for dynamic behavior and logic, you'll often want to give your components an _internal state_.
|
|
4
|
-
|
|
5
|
-
Some key concepts to keep in mind regarding Component state:
|
|
6
|
-
|
|
7
|
-
- The component state is defined within the `state` key of the Component configuration object.
|
|
8
|
-
- The `state` is expected to be a `function` (a regular function, not an ES6 arrow function), that returns an object representing the component's state.
|
|
9
|
-
- While defined on the configuration object, each component `instance` maintains its own distinct internal state.
|
|
10
|
-
- The structure of the `state` object is flexible, allowing you to define state values according to your component's requirements. These values can range from `strings`, `booleans`, `arrays`, to even nested `objects`.
|
|
11
|
-
- While you have full flexibility in structuring your state, be cautious about _excessive nesting_, as it could have a negative effect on performance.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
Example of defining a component state:
|
|
15
|
-
|
|
16
|
-
```js
|
|
17
|
-
export default Blits.Component('MyComponent', {
|
|
18
|
-
// ...
|
|
19
|
-
state() {
|
|
20
|
-
return {
|
|
21
|
-
active: false,
|
|
22
|
-
items: [],
|
|
23
|
-
style: {
|
|
24
|
-
positions: {
|
|
25
|
-
x: 100,
|
|
26
|
-
},
|
|
27
|
-
dimensions: {
|
|
28
|
-
h: 100,
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
color: 'tomato'
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
})
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Accessing State in Templates
|
|
38
|
-
|
|
39
|
-
As explained in the [template section](../essentials/template_syntax.md), you can refer to state variables in a template by prefixing the state variable name with a _dollar sign_ (e.g., `$alpha`). Think of the `$`-sign as a reference to the `this`-scope of the component.
|
|
40
|
-
|
|
41
|
-
For nested objects, you can use _dot notation_ (e.g., `$style.dimensions.w`).
|
|
42
|
-
|
|
43
|
-
## Accessing State in Component Code
|
|
44
|
-
|
|
45
|
-
In your component's code, you can reference state variables directly within the `this`-scope. For instance, a state variable named `color` can be accessed (and modified) by referencing `this.color`.
|
|
46
|
-
|
|
47
|
-
It's important to note that unlike in the template, you should _not_ use the dollar sign when accessing state variables within the component's code.
|
|
48
|
-
Also, remember that there is no need to explicitly reference the `state`-key. Blits automatically maps all state variables directly on the `this`-scope, for easy access.
|
|
49
|
-
|
|
50
|
-
The example below gives a full example of defining and using a component's state:
|
|
51
|
-
|
|
52
|
-
```js
|
|
53
|
-
export default Blits.Component('MyComponent', {
|
|
54
|
-
template: `
|
|
55
|
-
<Element :show="$active">
|
|
56
|
-
<Element :x="$style.x" w="100" :h="$style.dimensions.h">
|
|
57
|
-
<Element w="20" :h="$style.dimensions.h / 2" :color="$color" />
|
|
58
|
-
</Element>
|
|
59
|
-
</Element>
|
|
60
|
-
`,
|
|
61
|
-
state() {
|
|
62
|
-
return {
|
|
63
|
-
active: false,
|
|
64
|
-
items: [],
|
|
65
|
-
style: {
|
|
66
|
-
positions: {
|
|
67
|
-
x: 100,
|
|
68
|
-
},
|
|
69
|
-
dimensions: {
|
|
70
|
-
h: 100,
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
color: 'tomato'
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
hooks: {
|
|
77
|
-
ready() {
|
|
78
|
-
this.active = true
|
|
79
|
-
this.$setInterval(() => {
|
|
80
|
-
this.style.positions.x += 10
|
|
81
|
-
})
|
|
82
|
-
}
|
|
83
|
-
},
|
|
84
|
-
input: {
|
|
85
|
-
right() {
|
|
86
|
-
this.color = '#c0ffee'
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
})
|
|
90
|
-
```
|
|
1
|
+
# Component State
|
|
2
|
+
|
|
3
|
+
In Blits, a component can be as simple as a template with fixed, hardcoded values. However, for dynamic behavior and logic, you'll often want to give your components an _internal state_.
|
|
4
|
+
|
|
5
|
+
Some key concepts to keep in mind regarding Component state:
|
|
6
|
+
|
|
7
|
+
- The component state is defined within the `state` key of the Component configuration object.
|
|
8
|
+
- The `state` is expected to be a `function` (a regular function, not an ES6 arrow function), that returns an object representing the component's state.
|
|
9
|
+
- While defined on the configuration object, each component `instance` maintains its own distinct internal state.
|
|
10
|
+
- The structure of the `state` object is flexible, allowing you to define state values according to your component's requirements. These values can range from `strings`, `booleans`, `arrays`, to even nested `objects`.
|
|
11
|
+
- While you have full flexibility in structuring your state, be cautious about _excessive nesting_, as it could have a negative effect on performance.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
Example of defining a component state:
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
export default Blits.Component('MyComponent', {
|
|
18
|
+
// ...
|
|
19
|
+
state() {
|
|
20
|
+
return {
|
|
21
|
+
active: false,
|
|
22
|
+
items: [],
|
|
23
|
+
style: {
|
|
24
|
+
positions: {
|
|
25
|
+
x: 100,
|
|
26
|
+
},
|
|
27
|
+
dimensions: {
|
|
28
|
+
h: 100,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
color: 'tomato'
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Accessing State in Templates
|
|
38
|
+
|
|
39
|
+
As explained in the [template section](../essentials/template_syntax.md), you can refer to state variables in a template by prefixing the state variable name with a _dollar sign_ (e.g., `$alpha`). Think of the `$`-sign as a reference to the `this`-scope of the component.
|
|
40
|
+
|
|
41
|
+
For nested objects, you can use _dot notation_ (e.g., `$style.dimensions.w`).
|
|
42
|
+
|
|
43
|
+
## Accessing State in Component Code
|
|
44
|
+
|
|
45
|
+
In your component's code, you can reference state variables directly within the `this`-scope. For instance, a state variable named `color` can be accessed (and modified) by referencing `this.color`.
|
|
46
|
+
|
|
47
|
+
It's important to note that unlike in the template, you should _not_ use the dollar sign when accessing state variables within the component's code.
|
|
48
|
+
Also, remember that there is no need to explicitly reference the `state`-key. Blits automatically maps all state variables directly on the `this`-scope, for easy access.
|
|
49
|
+
|
|
50
|
+
The example below gives a full example of defining and using a component's state:
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
export default Blits.Component('MyComponent', {
|
|
54
|
+
template: `
|
|
55
|
+
<Element :show="$active">
|
|
56
|
+
<Element :x="$style.x" w="100" :h="$style.dimensions.h">
|
|
57
|
+
<Element w="20" :h="$style.dimensions.h / 2" :color="$color" />
|
|
58
|
+
</Element>
|
|
59
|
+
</Element>
|
|
60
|
+
`,
|
|
61
|
+
state() {
|
|
62
|
+
return {
|
|
63
|
+
active: false,
|
|
64
|
+
items: [],
|
|
65
|
+
style: {
|
|
66
|
+
positions: {
|
|
67
|
+
x: 100,
|
|
68
|
+
},
|
|
69
|
+
dimensions: {
|
|
70
|
+
h: 100,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
color: 'tomato'
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
hooks: {
|
|
77
|
+
ready() {
|
|
78
|
+
this.active = true
|
|
79
|
+
this.$setInterval(() => {
|
|
80
|
+
this.style.positions.x += 10
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
input: {
|
|
85
|
+
right() {
|
|
86
|
+
this.color = '#c0ffee'
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
```
|
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
# Computed properties
|
|
2
|
-
|
|
3
|
-
So far we've learned how to utilize a Component's [internal state](./component_state.md) and [props](./props.md) passed by a parent into a child component.
|
|
4
|
-
|
|
5
|
-
We've also seen how we can perform simple operations like `Math.max()` or `str.toUpperCase()` directly within an argument of a template on the Component's internal state or passed props.
|
|
6
|
-
However, as these operations become more complex it's more clear and more maintainable to abstract these operations into what we call _computed properties_.
|
|
7
|
-
|
|
8
|
-
Computed properties are a powerful tool for enhancing the readability of your component code. By abstracting complex or frequently used calculations from your template into computed properties, you can make your code more concise and easier to understand.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
## Defining computed properties
|
|
12
|
-
|
|
13
|
-
Within the `computed`-key of the Component configuration object, you can specify an object of `functions`. Each function name you define (`offset()` for example) becomes accessible as a _computed property_.
|
|
14
|
-
|
|
15
|
-
A computed property function should always _return_ a value.
|
|
16
|
-
|
|
17
|
-
In your template, you can reference these computed properties exactly the same as you would with _state_ variables and _props_, by prefixing them with a dollar sign (e.g., `$offset`).
|
|
18
|
-
|
|
19
|
-
In the rest of your app's code, you can access these computed properties (but not modify them!) using `this.offset`. Note that similar to Component state and props, you do not need to prefix with `computed` to access the computed property.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
```js
|
|
23
|
-
export default Blits.Component('MyComponent', {
|
|
24
|
-
// ...
|
|
25
|
-
computed: {
|
|
26
|
-
offset() {
|
|
27
|
-
return this.index * 100
|
|
28
|
-
},
|
|
29
|
-
bgColor() {
|
|
30
|
-
return this.focused === true ? 'aqua' : '#ccc'
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
})
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## Reactivity
|
|
37
|
-
|
|
38
|
-
Within a computed property, you can reference one or more state variables or props and _return_ a value based on calculations or logical operations.
|
|
39
|
-
|
|
40
|
-
Whenever the value of any of the referenced variables changes, the computed property will automatically recalculate.
|
|
41
|
-
|
|
42
|
-
If a computed property is referenced reactively in the template (i.e., the argument is prefixed with colons `:`), it will also trigger an automatic rerender of that portion of the template.
|
|
43
|
-
|
|
44
|
-
Computed properties should **not** have any _side effects_ (i.e. should not change the value of any state variable). Side effects can potentially lead your app into an endless loop if not handled carefully. If you want to execute certain logic upon state changes, you can consider to use a [watcher](./watchers.md) for this.
|
|
45
|
-
|
|
1
|
+
# Computed properties
|
|
2
|
+
|
|
3
|
+
So far we've learned how to utilize a Component's [internal state](./component_state.md) and [props](./props.md) passed by a parent into a child component.
|
|
4
|
+
|
|
5
|
+
We've also seen how we can perform simple operations like `Math.max()` or `str.toUpperCase()` directly within an argument of a template on the Component's internal state or passed props.
|
|
6
|
+
However, as these operations become more complex it's more clear and more maintainable to abstract these operations into what we call _computed properties_.
|
|
7
|
+
|
|
8
|
+
Computed properties are a powerful tool for enhancing the readability of your component code. By abstracting complex or frequently used calculations from your template into computed properties, you can make your code more concise and easier to understand.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## Defining computed properties
|
|
12
|
+
|
|
13
|
+
Within the `computed`-key of the Component configuration object, you can specify an object of `functions`. Each function name you define (`offset()` for example) becomes accessible as a _computed property_.
|
|
14
|
+
|
|
15
|
+
A computed property function should always _return_ a value.
|
|
16
|
+
|
|
17
|
+
In your template, you can reference these computed properties exactly the same as you would with _state_ variables and _props_, by prefixing them with a dollar sign (e.g., `$offset`).
|
|
18
|
+
|
|
19
|
+
In the rest of your app's code, you can access these computed properties (but not modify them!) using `this.offset`. Note that similar to Component state and props, you do not need to prefix with `computed` to access the computed property.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
```js
|
|
23
|
+
export default Blits.Component('MyComponent', {
|
|
24
|
+
// ...
|
|
25
|
+
computed: {
|
|
26
|
+
offset() {
|
|
27
|
+
return this.index * 100
|
|
28
|
+
},
|
|
29
|
+
bgColor() {
|
|
30
|
+
return this.focused === true ? 'aqua' : '#ccc'
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Reactivity
|
|
37
|
+
|
|
38
|
+
Within a computed property, you can reference one or more state variables or props and _return_ a value based on calculations or logical operations.
|
|
39
|
+
|
|
40
|
+
Whenever the value of any of the referenced variables changes, the computed property will automatically recalculate.
|
|
41
|
+
|
|
42
|
+
If a computed property is referenced reactively in the template (i.e., the argument is prefixed with colons `:`), it will also trigger an automatic rerender of that portion of the template.
|
|
43
|
+
|
|
44
|
+
Computed properties should **not** have any _side effects_ (i.e. should not change the value of any state variable). Side effects can potentially lead your app into an endless loop if not handled carefully. If you want to execute certain logic upon state changes, you can consider to use a [watcher](./watchers.md) for this.
|
|
45
|
+
|