@lightningjs/blits 0.5.8 → 0.5.10

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 CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ # v0.5.10
4
+
5
+ _3 jan 2024_
6
+
7
+ - Updated and improved documentation
8
+ - Added `screenResolution` and `pixelRatio` options to settings
9
+
10
+ # v0.5.9
11
+
12
+ _21 dec 2023_
13
+
14
+ - Changed transition-end callback to receive real prop value from node
15
+ - Implemented symbol for `wrapper` on Component (freeing up the name wrapper to be used in Component state)
16
+ - Added functionality to prevent a route ending up in the history stack (route option: `inHistory: false`)
17
+ - Improved router backtracking logic
18
+ - Added functionality to override route options during navigation
19
+ - Upgraded to latest version of the renderer (0.6.1) which contains a fix for animations not finishing correctly
20
+
3
21
  # v0.5.8
4
22
 
5
23
  _15 dec 2023_
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Blits - Lightning 3 App Development Framework
4
4
 
5
- With Blits (formaly know as Bolt), the App Development Framework for Lightning 3, it becomes a breeze to build great Lightning 3 applications.
5
+ With Blits, the App Development Framework for Lightning 3, it becomes a breeze to build great Lightning 3 applications.
6
6
 
7
7
  Blits is built on top of the Lightning 3 Rendererer and aims to provide a **great developer experience**, making it fun and easy to build your apps!
8
8
 
@@ -31,6 +31,6 @@
31
31
  "vite": "^4.0.4"
32
32
  },
33
33
  "dependencies": {
34
- "@lightningjs/blits": "^0.5.8"
34
+ "@lightningjs/blits": "^0.5.10"
35
35
  }
36
36
  }
package/docs/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Blits - Lightning 3 App Development Framework
2
2
 
3
- With Blits (formerly know as Bolt), the App Development Framework for Lightning 3, it becomes a breeze to build great Lightning 3 applications.
3
+ With Blits, the App Development Framework for Lightning 3, it becomes a breeze to build great Lightning 3 applications.
4
4
 
5
5
  Blits is built on top of the Lightning 3 Renderer and aims to provide a **great developer experience**, making it fun and easy to build your apps!
6
6
 
@@ -12,7 +12,9 @@ The Blits framework uses a **readable** XML-style template syntax and comes with
12
12
 
13
13
  You may be wondering where the name Blits comes from?
14
14
 
15
- Firstly, it short, memorable and has a nice ring to it. The name is derived from the word "Blitz", which means Lightning in German. Additionally, "Blits" is a Dutch term (a significant portion of the Lightning Open Source teams is based in the Netherlands) used to showcase something great or simply refer to things that are cool.
15
+ Firstly, it's short, memorable and just has a nice ring to it.
16
+
17
+ "Blits" is a Dutch word (a significant portion of the Lightning Open Source teams is based in the Netherlands). It means _flashy_ and it's often used to for things that are _great_ or _cool_.
16
18
 
17
19
  Finally it's a nerdy reference to the computer term "Bit Blits" commonly used in graphics processing, for rapidly moving a block of data into memory - quite similar to what Lightning is doing 🙂
18
20
 
@@ -28,11 +30,12 @@ Finally it's a nerdy reference to the computer term "Bit Blits" commonly used in
28
30
  - [Computed properties](computed_properties.md)
29
31
  - [Watching for changes](watchers.md)
30
32
  - [Styling and positioning Elements](element_attributes.md)
33
+ - [Displaying images](displaying_images.md)
34
+
31
35
  <!---
32
36
  - More complex logic in methods
33
37
  - Transitions
34
38
  - Implementing Custom components
35
39
  - Handling Text
36
- - Handling Images
37
40
  - Routing
38
41
  -->
package/docs/_sidebar.md CHANGED
@@ -8,4 +8,5 @@
8
8
  - [Computed properties](computed_properties.md)
9
9
  - [Watching for changes](watchers.md)
10
10
  - [Styling and positioning Elements](element_attributes.md)
11
+ - [Displaying images](displaying_images.md)
11
12
 
@@ -2,20 +2,60 @@
2
2
 
3
3
  ## Component State
4
4
 
5
- In Blits, a component can start as simple as a template with fixed, hardcoded values. However, for dynamic behavior and logic, you'll often want to incorporate an internal state within your components.
5
+ 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 extend your components with an _internal state_.
6
6
 
7
- Some key concepts to keep in mind:
7
+ Some key concepts to keep in mind regarding Component state:
8
8
 
9
- - Each component instance maintains its own distinct internal state.
9
+ - Each component _instance_ maintains its own distinct internal state.
10
10
  - The component state is defined within the `state` key of the Component configuration object.
11
- - 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.
11
+ - 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.
12
12
  - 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`.
13
13
  - While flexibility in structuring your state is advantageous, be cautious about excessive nesting, as it could have a negative effect on performance.
14
14
 
15
15
  ### Accessing State in Templates
16
16
 
17
- As explained in the template section, you can refer to state variables in your Element's arguments by prefixing the state variable name with a dollar sign (e.g., `$alpha`). For nested objects, you can use dot notation (e.g., `$style.w`).
17
+ As explained in the [template section](./template_syntax.md), you can refer to state variables in an Element's arguments by prefixing the state variable name with a _dollar sign_ (e.g., `$alpha`). For nested objects, you can use_dot notation_ (e.g., `$style.dimensions.w`).
18
18
 
19
19
  ### Accessing State in Component Code
20
20
 
21
- In your component's code, you can reference state variables within the `this` scope. For instance, a state variable named `color` can be accessed and modified by referencing `this.color`. 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.
21
+ In your component's code, you can reference state variables within the `this` scope. For instance, a state variable named `color` can be accessed and modified by referencing `this.color`. 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.
22
+
23
+ ```js
24
+ export default Blits.Component('MyComponent', {
25
+ template: `
26
+ <Element :show="$active">
27
+ <Element :x="$style.x" w="100" :h="$style.dimensions.h">
28
+ <Element w="20" :h="$style.dimensions.h / 2" :color="$color" />
29
+ </Element>
30
+ </Element>
31
+ `,
32
+ state() {
33
+ return {
34
+ active: false,
35
+ items: [],
36
+ style: {
37
+ positions: {
38
+ x: 100,
39
+ },
40
+ dimensions: {
41
+ h: 100,
42
+ },
43
+ },
44
+ color: 'tomato'
45
+ }
46
+ }
47
+ hooks: {
48
+ ready() {
49
+ this.active = true
50
+ this.$setInterval(() => {
51
+ this.style.positions.x += 10
52
+ })
53
+ }
54
+ },
55
+ input: {
56
+ right() {
57
+ this.color = '#c0ffee'
58
+ }
59
+ }
60
+ })
61
+ ```
@@ -6,7 +6,6 @@ Components in Blits follow a clearly defined structure, which helps keep your co
6
6
 
7
7
  This universal structure is also valuable for the interoperability of components.
8
8
 
9
-
10
9
  ### Creating a new Component
11
10
 
12
11
  Let's see how to create a new component and explore the basic anatomy of a component.
@@ -15,13 +14,45 @@ A new component is created using the `Blits.Component()` function. This function
15
14
 
16
15
  1. The first argument is the name of the component, which is mainly used for debugging purposes. It's essential to choose a unique and descriptive name for your component, such as `Homepage`, `Loader` or `SidebarMenuItem`.
17
16
 
18
- 2. The second argument is a configuration object, represented as an `object literal`. This configuration object can contain a predefined set of key-value pairs, including for example:
17
+ 2. The second argument is a _configuration object_, represented as an `object literal`. This configuration object can contain a predefined set of key-value pairs, including for example:
19
18
 
20
19
  - `template`: to define the template for the component.
21
20
  - `state`: to specify the component instance's internal state.
22
- - `prop`: to define the props that can be passed into the component.
21
+ - `props`: to define the props that can be passed into the component.
23
22
  - `hooks`: allowing you to hook into different lifecycle events of the component.
24
23
  - `input`: used to tap into user input.
24
+ - `methods`: used to define more complex functionality of the component
25
+
26
+ ```js
27
+ // src/components/Loader.js
28
+ export default Blits('Loader', {
29
+ template: `
30
+ <Element x="880" y="500" :show="$active">
31
+ <Element x="$offset" w="40" h="40" color="#94a3b8" :alpha="$alpha" />
32
+ </Element>
33
+ `,
34
+ props: ['type', 'size'],
35
+ state() {
36
+ return {
37
+ active: false,
38
+ alpha: 0.4,
39
+ offset: 100
40
+ }
41
+ },
42
+ hooks: {
43
+ ready() {
44
+ this.$setTimeout(() => {
45
+ this.active = true
46
+ }, 300)
47
+ }
48
+ },
49
+ input: {
50
+ enter() {
51
+ this.alpha = 1
52
+ }
53
+ }
54
+ })
55
+ ```
25
56
 
26
57
  In the next sections, we will go into the details of each of the component configuration options.
27
58
 
@@ -0,0 +1,69 @@
1
+ # Blits - Lightning 3 App Development Framework
2
+
3
+ ## Displaying Images
4
+
5
+
6
+ Now that we've covered the basics of creating Elements, as well as styling and positioning them, it's time to move away from these boring rectangles and explore how you can incorporate _images_ into your App.
7
+
8
+ In Blits is very easy to display an image. Simply add the `src` attribute to an Element, specifying the image's location.
9
+
10
+ For local images, make sure to place them in the `public` folder (e.g., `public/assets/background.jpg` or `public/images/logo.png`) and refer to them with a _relative_ path (omitting the `public` folder as root).
11
+
12
+ Remote images are also supported and can be linked directly (e.g., `http://mycdn.com/artists/jimi_hendrix/woodstock.jpg`).
13
+
14
+ ```xml
15
+ <Element src="images/logo.png" w="100" h="80" />
16
+ <Element src="http://mycdn.com/artists/jimi_hendrix/woodstock.jpg" w="1920" h="1080" />
17
+ ```
18
+
19
+ ### Sizing and Scaling
20
+
21
+ Make sure to give your Element a width (`w` ) and a height (`h`) attribute. Blits will scale the image to fit these exact dimensions.
22
+
23
+ For the best performance, it's important to keep your source images as small as possible. If you're displaying an image at `200px x 200px`, make sure the image is exactly that size or _smaller_. The latter option may lead to some quality loss.
24
+
25
+ ### Colorization
26
+
27
+ You also have the option to _colorize_ an image on the fly. Just add a `color` attribute to the Element with a `src` attribute. You can use a single color, or apply a gradient. By default all Elements with a `src` attribute get the a solid white background, which corresponds with the actual colors of the image.
28
+
29
+ ```xml
30
+ <Element
31
+ w="200"
32
+ h="300"
33
+ :src="$src"
34
+ color="{top: '#fff', bottom: '#000'}"
35
+ />
36
+ ```
37
+
38
+ ### Asynchronous Loading
39
+
40
+ All images are loaded asynchronously (and can possibly fail to load), even those local to your App. Blits allows you to easily hook into the `loaded` and `error` events of image Elements.
41
+
42
+ You can use this, for example, to only make something visible once an image is fully loaded. Or to display a fallback image when an remote image can't be retrieved.
43
+
44
+ ```xml
45
+ <Element src="http://mycdn.com/lightning.jpg" @loaded="$revealPage" @error="$showFallback" />
46
+ ```
47
+
48
+ > Note how events are prefixed with a `@` sign.
49
+
50
+ Considering the template above, in the `methods` key of your Component configuration object, you would then do something like:
51
+
52
+ ```js
53
+ {
54
+ //...
55
+ methods: {
56
+ revealPage(dimensions) {
57
+ this.$log.info('Image dimensions', dimensions.w, dimensions.h)
58
+ this.show = true
59
+ },
60
+ showFallback(error) {
61
+ this.$log.error('Image failed to load', error)
62
+ this.showBackupImage()
63
+ }
64
+ }
65
+ }
66
+ ```
67
+
68
+ The `loaded` event receives the images dimensions as it's argument and the `error` event receives an error message explaining the failure.
69
+
@@ -92,7 +92,9 @@ The value of alpha is also being applied recursively to the children of the Elem
92
92
 
93
93
  ### Rotation and scaling
94
94
 
95
- If you want to rotate an Element, you can use the `rotation` attribute. The rotation attribute in Blits accepts values in degrees. The rotation of an Element is also automatically applied to any children down the tree.
95
+ If you want to rotate an Element, you can use the `rotation` attribute. The rotation attribute in Blits accepts values in _degrees_.
96
+
97
+ The rotation of an Element is also automatically applied to any children down the tree.
96
98
 
97
99
  ```xml
98
100
  <Element w="200" h="200" color="blue" rotation="90">
@@ -100,13 +102,20 @@ If you want to rotate an Element, you can use the `rotation` attribute. The rota
100
102
  </Element>
101
103
  ```
102
104
 
103
- For scaling an Element the `scale` attribute is used. This attribute can be any numeric value starting at `0`. The default value is `1`, which means no scaling. Any value below `1` will scale down the element and values greater than `1` will scale the element up.
105
+ For scaling an Element the `scale` attribute is used.
106
+
107
+ This attribute either accepts a single numeric value for scaling evenly accross width and height. Or an _object literal_, if you want to apply a different scaling for the `x` and the `y` axis.
108
+
109
+ The value should start at `0` and the default value is `1`, which means no scaling.
110
+
111
+ Any value below `1` will scale _down_ the element and values greater than `1` will scale the element _up_.
104
112
 
105
113
  Similar to rotation, scale is also applied recursively to children down the tree of the Element that has it's `scale` attribute set.
106
114
 
107
115
  ```xml
108
116
  <Element w="200" h="200" color="blue" scale="0.5" />
109
117
  <Element w="200" h="200" color="#000" scale="2.3" />
118
+ <Element w="200" h="200" color="#000" scale="{x: 1, y: 3.14}" />
110
119
  ```
111
120
 
112
121
  ### Mounting point
@@ -119,20 +128,27 @@ In order to align the position starting at the _bottom-right_ corner, we would s
119
128
 
120
129
  If you omit either the `x` or the `y` key from the _object literal_, it's value will default to `0`.
121
130
 
131
+ In the case where the `x` and `y` values are the same (i.e. centering with `{x: 0.5, y: 0.5}`), you can also just supply a single value (`mount="0.5"`) instead of the object literal notation.
132
+
122
133
  ```xml
123
134
  <Element w="200" h="200" x="20" y="100" color="#333" mount="{x: 0.5, y: 0.8}" />
124
135
  <Element w="200" h="200" x="800" y="400" color="#333" mount="{y: 1}" />
136
+ <Element w="200" h="200" x="800" y="700" color="#333" mount="0.5" />
125
137
  ```
126
138
 
127
139
  ### Pivot point
128
140
 
129
- The pivot point of an Element comes into play when it's rotated or scaled. The pivot point defaults to the center of the Element, which means that when setting `rotation` it rotates around the middle. And when the Element is scaled it scales from the center out.
141
+ The pivot point of an Element comes into play when it's rotated or scaled. The pivot point defaults to the _center_ of the Element, which means that when setting `rotation` it rotates around the middle. And when the Element is scaled it scales from the center out.
130
142
 
131
143
  But sometimes you may want rotate around the left corner, or scale from the right side out. This can be controlled by adding the `pivot` attribute to the Element and, similar to the `mount` attribute, specify an _object literal_ with a `x` and a `y` key.
132
144
 
133
145
  Both `y` and `x` values should be anything between `0` and `1`, where `{x: 0, y: 0}` sets the pivot point to the _top-left_ corner and `{x: 1, y: 1}` refers to the _bottom-right_ corner. The default pivot value is `{x: 0.5, y: 0.5}` (i.e. the center) and if you omit `x` or `y` in your pivot object, it will default to `0.5`.
134
146
 
147
+ In the case where the `x` and `y` values are the same, you can also just supply a single value (`pivot="0.9"`) instead of the object literal notation.
148
+
149
+
135
150
  ```xml
136
151
  <Element w="200" h="200" x="20" y="100" pivot="{x: 0.5, y: 0.8}" rotation="69" />
137
152
  <Element w="200" h="200" x="800" y="400" pivot="{y: 1}" scale="3" />
153
+ <Element w="200" h="200" x="800" y="700" pivot="0.9" rotation="42" />
138
154
  ```
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Hooking into Lifecycle Events
4
4
 
5
- In Blits, components follow a defined flow of lifecycle events. Developers can integrate their logic into these events by specifying them in the `hooks` key of the Component configuration object.
5
+ Blits components follow a defined flow of lifecycle events. You can integrate specific logic into these events by specifying them in the `hooks` key of the Component configuration object.
6
6
 
7
7
  For each available lifecycle event, you can define a function with the same name as the event itself. This function will execute each time the event is triggered for your component's instance. It's essential to use a regular function instead of an arrow function if you intend to access the `this` scope of your component instance.
8
8
 
@@ -18,11 +18,9 @@ For each available lifecycle event, you can define a function with the same name
18
18
 
19
19
  - **destroy()**: The `destroy` event fires when the component is being destroyed and removed. This event provides an opportunity to perform any custom cleanup operations before the component is removed.
20
20
 
21
- ### Example
22
-
23
- ```javascript
24
- {
25
- // Other component configurations...
21
+ ```js
22
+ export default Blits.Component('MyComponent', {
23
+ // ...
26
24
  hooks: {
27
25
  init() {
28
26
  // Logic to execute on init event
@@ -40,7 +38,5 @@ For each available lifecycle event, you can define a function with the same name
40
38
  // Logic to execute on destroy event
41
39
  },
42
40
  },
43
- };
41
+ })
44
42
  ```
45
-
46
- In the provided example, each function within the `hooks` object corresponds to a specific lifecycle event, allowing developers to inject custom logic at different stages of the component's lifecycle.
package/docs/props.md CHANGED
@@ -25,9 +25,11 @@ For more advanced usage, you can define props using an array of objects for each
25
25
  - Mark the prop as _required_.
26
26
  - Apply a `cast` function to modify the value passed as a prop.
27
27
 
28
- ```javascript
29
- {
28
+ ```js
29
+ export default Blits.Component('MyComponent', {
30
+ // ...
30
31
  props: [
32
+ 'color',
31
33
  {
32
34
  key: 'alpha',
33
35
  default: 0.5,
@@ -38,7 +40,7 @@ For more advanced usage, you can define props using an array of objects for each
38
40
  cast: Number
39
41
  }
40
42
  ]
41
- }
43
+ })
42
44
  ```
43
45
 
44
46
  You can mix and match the simple string notation with the more advanced object notation within the same `props` array.
@@ -2,15 +2,13 @@
2
2
 
3
3
  ## Template structure
4
4
 
5
- Blits uses an easy-to-read XML-style template syntax. The syntax is inspired by frameworks like VueJS, so developers familiar with Vue will recognize certain concepts.
5
+ Blits uses an easy-to-read _XML-style_ template syntax. The syntax is inspired by frameworks like VueJS, so developers familiar with Vue will recognize certain concepts.
6
6
 
7
- The template key in the configuration object holds a string with an XML-like structure. Templates often span multiple lines, so it's advisable to use a JS template literal.
7
+ The _template_ key in the Componentn configuration object holds a string with an XML-like structure. Templates often span multiple lines, so it's advisable to use a JS template literal.
8
8
 
9
9
  You can use different tags in your templates. And similar to HTML you can use arguments and nested tags. Self-closing tags and HTML-style comments are also supported.
10
10
 
11
- The default tag within a template is the `<Element>` tag, which corresponds to a node in the Lightning 3 renderer. For each element, you can define a set of predefined render properties, such as `x`, `y`, `w`, `color`, and more. Detailed information regarding the properties supported by the <Element> tag can be found here.
12
-
13
- As your component grows in complexity, you may want to organize your template with custom components, abstracting both templates and logic. Inside a component template, you can utilize sub-components (i.e. a `Menu` component) and reference them with tags like `<Menu></Menu>`.
11
+ The default tag within a template is the `<Element>` tag, which corresponds to a node in the Lightning 3 renderer. For each element, you can define a set of predefined render properties, such as `x`, `y`, `w`, `color`, and more. Detailed information regarding the attributes supported by the <Element> tag can be found [here](./element_attributes.md).
14
12
 
15
13
  Consider the following template example:
16
14
 
@@ -22,10 +20,46 @@ Consider the following template example:
22
20
  </Element>
23
21
  ```
24
22
 
23
+ ### Hardcoded arguments
24
+
25
25
  In this template, you'll observe certain arguments with hardcoded values. You use these to configure the parts of your component that never change.
26
26
 
27
+ ### Dynamic arguments
28
+
27
29
  Additionally, some arguments contain a value prefixed with a dollar sign (`$`), representing _dynamic_ arguments. When the component renders, it automatically populates these dynamic values based on the component's state (or props or computed properties).
28
30
 
31
+ ### Reactive arguments
32
+
29
33
  Furthermore, certain arguments are prefixed with a colon sign (`:`), indicating _reactive_ arguments. These are not only dynamically set when your component renders, but also _rerender_ the corresponding part of the template whenever the referenced value in your component's internal state changes. This reactive data binding eliminates the need for manual template patching, as seen in Lightning 2.
30
34
 
31
35
  Reactive arguments support _interpolation_, enabling the use of simple JavaScript instructions, such as ternary conditions or basic string and Math manipulations. For more complex logic, it's recommended to abstract this into a Component method or a Computed property.
36
+
37
+ ## Custom components
38
+
39
+ As your component grows in complexity, you may want to reorganize your codebase with custom components, abstracting and reusing both templates and logic.
40
+
41
+ Inside a component template, you can utilize sub-components (i.e. a `Menu` component) and reference them with tags like `<Menu></Menu>`.
42
+
43
+ In order for the `<Menu />` tag to become available for use in a component's template, you first have to `import` the file that exports that component.
44
+
45
+ Next you have to _register_ this imported component using the `components`-key in the configuration object. The `components` key is an _object literal_, where the value is the reference to the Blits component. The key will correspond with the _tag_ that can be used to invoke the component.
46
+
47
+ ```js
48
+
49
+ import Menu from './components/menu.js'
50
+ import Button from './components/button.js'
51
+
52
+ export default Blits.Component('MyComponent', {
53
+ components: {
54
+ Menu, // makes <Menu /> available for use in the template
55
+ MyButton: Button, // makes Button available as <MyButton /> in the template
56
+ },
57
+ template: `
58
+ <Element>
59
+ <Menu />
60
+ <MyButton />
61
+ </Element>
62
+ `
63
+ })
64
+
65
+ ```
@@ -2,18 +2,25 @@
2
2
 
3
3
  ## Handling User Input
4
4
 
5
- You don't want your Lightning 3 App to be completely static, instead users should be able to navigate and interact with the app, often using devices like a remote control.
5
+ When building an App you probably want users to interact with it as well, for example to enable them to navigate through your App. In other words, you will want to handle _user input_. In the context of a Blits / Lightning App this often will be key input via a remote control.
6
6
 
7
- Blits offers a straightforward interface to handle user input for each component.
7
+ Blits offers an intuitive and straightforward interface to handle key input in components.
8
+
9
+ ### Focus
10
+
11
+ Before diving into the specifics of key handling, it is important to understand the concept of _focus_. In a Blits app, there is always one Component that has the focus. By default this will be the root App component.
12
+
13
+ The component that currently has focus, is the one that is responsible for handling the user input at that moment. For example, when a user clicks the _right_ or _left_ button while a _Poster Component_ has focus, it is that instance of the Poster Component that will initially _receive_ the key press event.
8
14
 
9
15
  ### Configuring Input Handling
10
16
 
11
- Within the Component configuration object, the `input` key is utilized to define how the component should react to specific key presses when it has focus. It should contain an object of functions that specify the actions to take based on key presses.
17
+ Within the Component configuration object, the `input` key is used to define how the component should react to specific key presses when it has focus. The `input` key should be an `object literal` of `functions` for each input event that the component wants to handle.
12
18
 
13
- Each function should correspond to a key name, such as `up`, `down`, `enter`, `space`, `back`, `1`, `2`, `a` etc. Each function defined in the `input` object receives the full `InputEvent` object as its first argument.
19
+ Each function corresponds to a _key name_, such as `up`, `down`, `enter`, `space`, `back`, `1`, `2`, `a` etc. Each function defined in the `input` object, receives the full `InputEvent` object as its first argument.
14
20
 
15
21
  ```javascript
16
- {
22
+ export default Blits.Component('MyComponent', {
23
+ // ...
17
24
  input: {
18
25
  up() {
19
26
  // Logic to execute when users press up
@@ -30,9 +37,9 @@ Each function should correspond to a key name, such as `up`, `down`, `enter`, `s
30
37
 
31
38
  ### Event Handling Chain
32
39
 
33
- If the currently focused component does not handle a key press, the framework will traverse up the component hierarchy, checking if any parent components have defined functions for that key press in their `input`-object. This event handling chain continues until it reaches the root Application.
40
+ If the currently focused component does not handle a key press, Blits will traverse up the component hierarchy, checking for any _parent_ component that does have a function defined for that key press in the `input`-key. This input event handling chain continues until it reaches the root App component.
34
41
 
35
- When a component handles a key press by having a corresponding function specified, the event handling chain stops by default. However, if you want the input event to propagate up the hierarchy, you can set focus to the parent element and pass the `InputEvent` object on in the function call.
42
+ When a component handles a key press by having a corresponding function specified, the event handling chain stops by default. However, if you want the input event to propagate up the hierarchy, you can set focus to the parent element and pass the `InputEvent` object on in that function call.
36
43
 
37
44
  ```javascript
38
45
  {
@@ -51,7 +58,7 @@ When a component handles a key press by having a corresponding function specifie
51
58
 
52
59
  ### Catch-All Handling
53
60
 
54
- To allow a focused component to respond to any key and act as a catch-all, you can add an `any()` function to the input object.
61
+ To allow a focused component to respond to any key and act as a _catch-all_, you can add an `any()` function to the input object.
55
62
 
56
63
  ```javascript
57
64
  {
@@ -62,3 +69,59 @@ To allow a focused component to respond to any key and act as a catch-all, you c
62
69
  }
63
70
  }
64
71
  ```
72
+
73
+ ### Custom Keycode mapping
74
+
75
+ Blits comes with a default keycode mapping. This mapping is a sensible default that works in your desktop browser and with most RDK based devices.
76
+
77
+ But it's possible that the keycodes and mapping of your target device are slightly or even completely different.
78
+
79
+ In Blits you can easily configure the key mapping to match your needs. In the `src/index.js` file where we instaniate the App via the `Blits.Launch` function, we can add an extra key, called `keys`, to the _settings object_.
80
+
81
+ The `'key` item should be an object literal with _key value pairs_. As the `key`, you should provide the `key` or `keyCode` of the `KeyboardEvent` and the `value` should be the name of the event as used in the `input` object of the Component configuration object.
82
+
83
+ > You can use a site like [keyjs.dev](https://keyjs.dev/) to find the key and keyCode
84
+
85
+ ```js
86
+ // src/index.js
87
+ Blits.Launch(App, 'app', {
88
+ w: 1920,
89
+ h: 1080,
90
+ //...
91
+ keys: {
92
+ // switch left and right using the key
93
+ ArrowLeft: 'right',
94
+ ArrowRight: 'left',
95
+ // switch up and down using the keyCode
96
+ 38: 'down',
97
+ 40: 'up',
98
+ // register new handlers
99
+ '.': 'dot', // dot() can now be used in the input object
100
+ // key code for letter 's'
101
+ 83: 'search' // search() can now be used in the input object
102
+ }
103
+ })
104
+ ```
105
+
106
+ The custom keys object will be merged with the default key mapping, that looks like this:
107
+
108
+ ```js
109
+ const defaultKeyMap = {
110
+ ArrowLeft: 'left',
111
+ ArrowRight: 'right',
112
+ ArrowUp: 'up',
113
+ ArrowDown: 'down',
114
+ Enter: 'enter',
115
+ ' ': 'space',
116
+ Backspace: 'back',
117
+ Escape: 'escape',
118
+ 37: 'left',
119
+ 39: 'right',
120
+ 38: 'up',
121
+ 40: 'down',
122
+ 13: 'enter',
123
+ 32: 'space',
124
+ 8: 'back',
125
+ 27: 'escape',
126
+ }
127
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightningjs/blits",
3
- "version": "0.5.8",
3
+ "version": "0.5.10",
4
4
  "description": "Blits: The Lightning 3 App Development Framework",
5
5
  "bin": "bin/index.cjs",
6
6
  "exports": {
@@ -42,7 +42,7 @@
42
42
  "tape": "^5.5.0"
43
43
  },
44
44
  "dependencies": {
45
- "@lightningjs/renderer": "^0.6.0",
45
+ "@lightningjs/renderer": "^0.6.1",
46
46
  "@lightningjs/vite-plugin-import-chunk-url": "^0.3.0"
47
47
  }
48
48
  }
package/src/component.js CHANGED
@@ -121,7 +121,7 @@ const Component = (name = required('name'), config = required('config')) => {
121
121
  }
122
122
 
123
123
  this.parent = parentComponent
124
- this.wrapper = parentEl
124
+ this[symbols.wrapper] = parentEl
125
125
 
126
126
  Object.defineProperties(this, {
127
127
  type: {
package/src/element.js CHANGED
@@ -163,7 +163,7 @@ const Props = {
163
163
  this._set.add('scale')
164
164
  },
165
165
  set show(v) {
166
- this.alpha = v ? 1 : 0
166
+ this._props.alpha = v ? 1 : 0
167
167
  },
168
168
  set alpha(v) {
169
169
  this._props.alpha = v
@@ -317,7 +317,7 @@ const Element = {
317
317
  // fire transition end callback if specified
318
318
  transition.end &&
319
319
  typeof transition.end === 'function' &&
320
- transition.end.call(this.component, this, prop, props[prop])
320
+ transition.end.call(this.component, this, prop, this.node[prop])
321
321
  })
322
322
  .then(resolve)
323
323
  } catch (e) {
package/src/launch.d.ts CHANGED
@@ -51,6 +51,8 @@ interface SdfFont {
51
51
  png: string
52
52
  }
53
53
 
54
+ type ScreenResolutions = 'hd' | '720p' | 720 | 'fhd' | 'fullhd' | '1080p' | 1080 | '4k' | '2160p' | 2160
55
+
54
56
  type Font = WebFont | SdfFont
55
57
 
56
58
  export type DebugLevel = 0 | 1 | 2
@@ -95,7 +97,45 @@ export interface Settings {
95
97
  /**
96
98
  * Mode of reactivity (`Proxy` or `defineProperty`)
97
99
  */
98
- reactivityMode?: ReactivityModes
100
+ reactivityMode?: ReactivityModes,
101
+ /**
102
+ * Screen resolution of the device, defining the pixelRatio used to convert dimensions
103
+ * and positions in the App code to the actual device logical coordinates
104
+ *
105
+ * If not supplied, Blits will try to autodetect the device screen resolution. Otherwise
106
+ * the exact dimensions and positions used the app code are used.
107
+ *
108
+ * Note: If the option `pixelRatio` is specified in the Settings object, this value will take presedence
109
+ * over the screen resolution setting.
110
+ *
111
+ * Currently 3 screen resolutions are supported, which can be defined with different alias values:
112
+ *
113
+ * For 720x1080 (1px = 0.66666667px)
114
+ * - hd
115
+ * - 720p
116
+ * - 720
117
+ *
118
+ * For 1080x1920 (1px = 1px)
119
+ * - fhd
120
+ * - fullhd
121
+ * - 1080p
122
+ * - 1080
123
+ *
124
+ * For 2160x3840 (1px = 2px)
125
+ * - 4k
126
+ * - 2160p
127
+ * - 2160
128
+ */
129
+ screenResolution?: ScreenResolutions,
130
+ /**
131
+ * Custom pixel ratio of the device used to convert dimensions
132
+ * and positions in the App code to the actual device logical coordinates
133
+ *
134
+ * Takes presedence over the `screenResolution` setting
135
+ *
136
+ * Defaults to 1 if not specified
137
+ */
138
+ pixelRatio?: number
99
139
  }
100
140
 
101
141
  /**
package/src/launch.js CHANGED
@@ -19,7 +19,7 @@ import { MainCoreDriver, RendererMain } from '@lightningjs/renderer'
19
19
  // import RendererWorker from '@lightningjs/renderer/workers/renderer?worker'
20
20
  import Settings from './settings.js'
21
21
  import { initLog, Log } from './lib/log.js'
22
-
22
+ import { screenResolutions } from './lib/utils.js'
23
23
  // import coreExtensionModule from './fontLoader.js?importChunkUrl'
24
24
 
25
25
  export let renderer
@@ -41,6 +41,11 @@ export default (App, target, settings) => {
41
41
  appWidth: settings.w || 1920,
42
42
  appHeight: settings.h || 1080,
43
43
  coreExtensionModule: settings.fontLoader,
44
+ deviceLogicalPixelRatio:
45
+ settings.pixelRatio ||
46
+ screenResolutions[settings.screenResolution] ||
47
+ screenResolutions[window.innerHeight] ||
48
+ 1,
44
49
  },
45
50
  target,
46
51
  driver
@@ -21,6 +21,17 @@ export default (component, routes) => {
21
21
  component.prototype[symbols.routes] = []
22
22
  Object.keys(routes).forEach((key) => {
23
23
  // todo: validate routes[key] for expected format etc.
24
- component.prototype[symbols.routes][key] = routes[key]
24
+ component.prototype[symbols.routes][key] = {
25
+ ...routes[key],
26
+ ...{
27
+ // merge default route options with route specific options
28
+ options: {
29
+ ...{
30
+ inHistory: true,
31
+ },
32
+ ...routes[key].options,
33
+ },
34
+ },
35
+ }
25
36
  })
26
37
  }
@@ -23,6 +23,7 @@ export default {
23
23
  type: Symbol('type'),
24
24
  watchers: Symbol('watchers'),
25
25
  watchKeys: Symbol('watchKeys'),
26
+ wrapper: Symbol('wrapper'),
26
27
 
27
28
  /* Utilizing the global Symbol registry for the following purposes */
28
29
 
@@ -0,0 +1,12 @@
1
+ export const screenResolutions = {
2
+ hd: 0.66666667,
3
+ '720p': 0.66666667,
4
+ 720: 0.66666667,
5
+ fhd: 1,
6
+ fullhd: 1,
7
+ '1080p': 1,
8
+ 1080: 1,
9
+ '4k': 2,
10
+ '2160p': 2,
11
+ 2160: 2,
12
+ }
@@ -27,6 +27,8 @@ export let navigating = false
27
27
 
28
28
  const cacheMap = new WeakMap()
29
29
  const history = []
30
+ let overrideOptions = {}
31
+ let navigatingBack = false
30
32
 
31
33
  export const getHash = () => {
32
34
  return (document.location.hash || '/').replace(/^#/, '')
@@ -38,7 +40,10 @@ export const matchHash = (path, routes = []) => {
38
40
  return r.path === path
39
41
  })
40
42
  .pop()
41
- if (route) currentRoute = route
43
+ if (route) {
44
+ route.options = { ...route.options, ...overrideOptions }
45
+ currentRoute = route
46
+ }
42
47
  return route
43
48
  }
44
49
 
@@ -50,7 +55,11 @@ export const navigate = async function () {
50
55
  const route = matchHash(hash, this.parent[symbols.routes])
51
56
 
52
57
  if (route) {
53
- if (history[history.length - 1] !== hash) history.push(hash)
58
+ // add the previous route (tecnhically still the current route at this point)
59
+ // into the history stack, unless navigating back or inHistory flag of route is false
60
+ if (navigatingBack === false && previousRoute && previousRoute.options.inHistory === true) {
61
+ history.push(previousRoute)
62
+ }
54
63
  // apply default transition if none specified
55
64
  if (!('transition' in route)) {
56
65
  route.transition = fadeInFadeOutTransition
@@ -82,7 +91,7 @@ export const navigate = async function () {
82
91
  view = view(this[symbols.props], holder, this)
83
92
  }
84
93
  } else {
85
- holder = view.wrapper
94
+ holder = view[symbols.wrapper]
86
95
  }
87
96
 
88
97
  this[symbols.children].push(view)
@@ -130,6 +139,9 @@ export const navigate = async function () {
130
139
  Log.error(`Route ${hash} not found`)
131
140
  }
132
141
  }
142
+
143
+ // reset navigating indicators
144
+ navigatingBack = false
133
145
  navigating = false
134
146
  }
135
147
 
@@ -139,11 +151,11 @@ const removeView = async (route, view, transition) => {
139
151
  if (Array.isArray(transition)) {
140
152
  for (let i = 0; i < transition.length; i++) {
141
153
  i === transition.length - 1
142
- ? await setOrAnimate(view.wrapper, transition[i])
143
- : setOrAnimate(view.wrapper, transition[i])
154
+ ? await setOrAnimate(view[symbols.wrapper], transition[i])
155
+ : setOrAnimate(view[symbols.wrapper], transition[i])
144
156
  }
145
157
  } else {
146
- await setOrAnimate(view.wrapper, transition)
158
+ await setOrAnimate(view[symbols.wrapper], transition)
147
159
  }
148
160
  }
149
161
 
@@ -169,14 +181,17 @@ const setOrAnimate = (node, transition, shouldAnimate = true) => {
169
181
  : node.set(transition.prop, transition.value)
170
182
  }
171
183
 
172
- export const to = (location) => {
184
+ export const to = (location, options = {}) => {
185
+ overrideOptions = options
173
186
  window.location.hash = `#${location}`
174
187
  }
175
188
 
176
189
  export const back = () => {
177
- if (history.length > 1) {
178
- to(history[history.length - 2])
179
- history.splice(-2, 2)
190
+ const route = history.pop()
191
+ if (route) {
192
+ // set indicator that we are navigating back (to prevent adding page to history stack)
193
+ navigatingBack = true
194
+ to(route.path)
180
195
  return true
181
196
  } else {
182
197
  return false