@lightningjs/blits 1.9.2 → 1.10.1
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 +19 -0
- package/docs/_sidebar.md +3 -0
- package/docs/built-in/layout.md +8 -0
- package/docs/router/basics.md +98 -0
- package/docs/router/hooks.md +70 -0
- package/docs/router/transitions.md +146 -0
- package/docs/sidebar.json +13 -0
- package/package.json +2 -2
- package/src/component.js +2 -1
- package/src/components/RouterView.js +5 -2
- package/src/components/RouterView.orig.js +5 -2
- package/src/engines/L3/element.js +35 -11
- package/src/focus.js +1 -0
- package/src/lib/codegenerator/generator.js +9 -1
- package/src/lib/codegenerator/generator.test.js +176 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v1.10.1
|
|
4
|
+
|
|
5
|
+
_15 nov 2024_
|
|
6
|
+
|
|
7
|
+
- Upgraded to renderer 2.7.1
|
|
8
|
+
- Added documentation on Router
|
|
9
|
+
|
|
10
|
+
## v1.10.0
|
|
11
|
+
|
|
12
|
+
_8 nov 2024_
|
|
13
|
+
|
|
14
|
+
- Fixed issue with back button not bubbling to App component after last page in router history is popped
|
|
15
|
+
- Added `align-items` attribute to Layout component
|
|
16
|
+
- Fixed issue with watching nested state variables and global state
|
|
17
|
+
- Upgraded to renderer 2.6.2
|
|
18
|
+
- Fixed issue with white background for Elements with falsy src attribute
|
|
19
|
+
- Fixed issue with calling focus on component that already is focussed
|
|
20
|
+
|
|
21
|
+
|
|
3
22
|
## v1.9.2
|
|
4
23
|
|
|
5
24
|
_5 nov 2024_
|
package/docs/_sidebar.md
CHANGED
|
@@ -24,6 +24,9 @@
|
|
|
24
24
|
- [Directives](/built-in/directives.md)
|
|
25
25
|
- [For loop](/built-in/for-loop.md)
|
|
26
26
|
- [Layout](/built-in/layout.md)
|
|
27
|
+
- Router
|
|
28
|
+
- [Basics](/router/basics.md)
|
|
29
|
+
- [Hooks](/router/hooks.md)
|
|
27
30
|
- Plugins
|
|
28
31
|
- [Text-to-Speech / Announcer](/plugins/text-to-speech-announcer.md)
|
|
29
32
|
- [Language](/plugins/language.md)
|
package/docs/built-in/layout.md
CHANGED
|
@@ -37,6 +37,14 @@ In order to align vertically use `<Layout direction="vertical"></Layout>`. And u
|
|
|
37
37
|
|
|
38
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
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 in 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
|
+
|
|
40
48
|
### Dynamic dimensions
|
|
41
49
|
|
|
42
50
|
For the Layout component to work properly, the direct children all need to have dimensions (i.e `w` and `h` attributes). The exception here being Text elements.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Router Basics
|
|
2
|
+
|
|
3
|
+
The Blits framework comes with a built-in, performant router that eliminates the need to pull in any additional dependencies into your App.
|
|
4
|
+
|
|
5
|
+
Before we dive into the details, let's first touch base on some key concepts.
|
|
6
|
+
|
|
7
|
+
## Key concepts
|
|
8
|
+
|
|
9
|
+
### Pages are regular Blits Components
|
|
10
|
+
|
|
11
|
+
The router in Blits is primarily used to navigate between Pages in your App. In the context of a Blits App, a Page is just a regular Blits Component. Usually it will be a Component that fills the entire screen. But it is also possible to have only a smaller portion of the screen contain dynamic routed content.
|
|
12
|
+
|
|
13
|
+
### URL hash
|
|
14
|
+
|
|
15
|
+
When navigating to a page, the hash in the URL of the browser will automatically be updated (i.e. `http://localhost:8080/#my/page`) to match the current page. The hash can also be used to _deep-link_ directly into your application. Upon first load, the router functionality will evaluate the URL hash and try to match that to a defined route. If a matching route is found, it will load that page as deep linked content.
|
|
16
|
+
|
|
17
|
+
## Defining Routes
|
|
18
|
+
|
|
19
|
+
Routes are defined in the main Application component, as an Array under the `routes` key of the configuration object.
|
|
20
|
+
|
|
21
|
+
Each route in this array, is an Object literal that includes the following key properties:
|
|
22
|
+
|
|
23
|
+
- `path` - the url hash to the route (i.e. `/my/page`)
|
|
24
|
+
- `component` - the Blits component to render for the route. This can be a direct reference, but also a promise or a dynamic import that resolves to a Blits component
|
|
25
|
+
- `hooks` (optional) - hooks such as `before` can be defined to execute code before navigating to the route
|
|
26
|
+
- `options` (optional) - additional options defining route behaviour, such as `keepAlive` and `inHistory`
|
|
27
|
+
- `transitions` (optional) - used to define custom transitions between pages
|
|
28
|
+
|
|
29
|
+
### Dynamic routes with params
|
|
30
|
+
|
|
31
|
+
Besides static routes such as `/account` and `/settings/wifi/advanced`, the Blits router also supports dynamic routes where URI parts can contain params.
|
|
32
|
+
|
|
33
|
+
To define a param in a route path prefix a string with a colon (`:`), followed by the name of the param, e.g. `/movies/:genre/:id`. This route path will match a URI hash such as `#/movies/sci-fi/65281918`. The router will take the params and inject them as a `prop` into the Page that's being navigated to.
|
|
34
|
+
|
|
35
|
+
To access these props in the Page component, they must first be defined in the Component first (i.e. `props: ['genre', 'id']`). Once defined, the values (`sci-fi` and `65281918`) in this case, will be available on the `this` scope, as with any regular Component.
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
## Router view
|
|
39
|
+
|
|
40
|
+
The routes are loaded into a `<RouterView />` component, which acts as a placeholder for the routed Page components.
|
|
41
|
+
|
|
42
|
+
The `<RouterView />` component can be positioned and sized as you would expect from a normal Blits Component.
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
export default Blits.Application({
|
|
47
|
+
template: `
|
|
48
|
+
<Element>
|
|
49
|
+
<RouterView x="300" y="200" w="1520" h="680" />
|
|
50
|
+
</Element>
|
|
51
|
+
`,
|
|
52
|
+
routes: [
|
|
53
|
+
{ path: '/', component: Home },
|
|
54
|
+
{ path: '/details', component: () => import('./pages/Details.js'), },
|
|
55
|
+
{ path: '/account',
|
|
56
|
+
component: () => {
|
|
57
|
+
// imagine this is an API call or some other async action
|
|
58
|
+
return new Promise((resolve) => {
|
|
59
|
+
resolve(Account)
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
// ...
|
|
65
|
+
})
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Navigation
|
|
69
|
+
|
|
70
|
+
Each component in a routed Blits app has a `this.$router` object that provides access to the Router instance. It can be used to programmatically navigate to pages, by calling the `to()` method on it.
|
|
71
|
+
|
|
72
|
+
The `this.$router.to()`-methods accepts 3 arguments:
|
|
73
|
+
|
|
74
|
+
- `path` - the path of the route to navigate to
|
|
75
|
+
- `data` (optional) - an object with data to pass into the page as `props`
|
|
76
|
+
- `options` (optional) - additional options defining route behaviour, such as `keepAlive` and `inHistory` (these will overwrite any options specified for a route in the routes array)
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
export default Blits.Component('Poster', {
|
|
80
|
+
input: {
|
|
81
|
+
enter() {
|
|
82
|
+
this.$router.to('/movie/details', {id: '1', img: 'details.png'}, {inHistory: false})
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Whenever you navigate to a new page, the URL hash will automatically be updated. Unless specified otherwise, navigating to a new page, will add that route to the history stack. The `back` input action is automatically wired up to navigate back down the history stack.
|
|
89
|
+
|
|
90
|
+
## Router API
|
|
91
|
+
|
|
92
|
+
The Router API provides several useful methods and properties for managing routes and navigation:
|
|
93
|
+
|
|
94
|
+
- `this.$router.to()` - navigating to a different location (as discussed above)
|
|
95
|
+
- `this.$router.back()` - programmatically navigate back down the history stack
|
|
96
|
+
- `this.$router.currentRoute` - retrieve the current route
|
|
97
|
+
- `this.$router.routes` - retrieve the list of all routes
|
|
98
|
+
- `this.$router.navigating` - retrieve the current navigating state
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Router Hooks
|
|
2
|
+
|
|
3
|
+
The Blits router supports hooks that allow you to execute code at specific points during the navigation process.
|
|
4
|
+
|
|
5
|
+
Hooks are defined per route in the optional `hooks` key within the Route configuration object.
|
|
6
|
+
|
|
7
|
+
> Currently, the Blits router only supports the before hook, with additional hooks planned for future releases.
|
|
8
|
+
|
|
9
|
+
## Before hook
|
|
10
|
+
|
|
11
|
+
The before hook is executed before the navigation occurs. It can be used for checks, such as verifying user authentication for a specific route, for pre-fetching data, or for executing logic like sending stats beacons.
|
|
12
|
+
|
|
13
|
+
The before hook is defined as a function that receives 2 arguments: the Route object that is being navigated `to`. And the Route object that is being navigated away `from`.
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
export default Blits.Application({
|
|
17
|
+
// ...
|
|
18
|
+
routes: [
|
|
19
|
+
{
|
|
20
|
+
path: '/',
|
|
21
|
+
component: Home,
|
|
22
|
+
hooks: {
|
|
23
|
+
before(to, from) {
|
|
24
|
+
// custom logic
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
// ...
|
|
30
|
+
})
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The before hook can optionally return a value, which will influence the behaviour of the route change:
|
|
34
|
+
|
|
35
|
+
- when `false` is returned, the router will fail to finish the routing, and act as if _no matching route was found_
|
|
36
|
+
- when a `string` is returned, the router will interpret this a _new route path to redirect to_
|
|
37
|
+
- when an `object` is returned, the router will interpret this as a route object, allowing to _modify (or completely replace) the route object_ being navigated to
|
|
38
|
+
|
|
39
|
+
Returning an `object` is a simple, yet powerful, mechanism to add advanced runtime configuration to your routes. A common scenario is to overwrite certain parts of the route object provided in the `to`-argument, such as the route options (such as `inHistory` or `stayAlive`) or define conditional page transitions.
|
|
40
|
+
|
|
41
|
+
It's also possible to add custom data to the route object in the `key` object. Custom data should be an object literal. When the new page loads, the keys in the `data` object are injected into the Component as `props`.
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
export default Blits.Application({
|
|
45
|
+
// ...
|
|
46
|
+
routes: [
|
|
47
|
+
{
|
|
48
|
+
path: '/video/details/:id',
|
|
49
|
+
component: Details,
|
|
50
|
+
hooks: {
|
|
51
|
+
before(to, from) {
|
|
52
|
+
// redirect to an error page when id is not a number
|
|
53
|
+
if(isNaN(to.params.id)) {
|
|
54
|
+
return '/not-valid'
|
|
55
|
+
}
|
|
56
|
+
// set a different transition when navigating from home page
|
|
57
|
+
if(from && from.path === '/home') {
|
|
58
|
+
to.transition = slideInTransition
|
|
59
|
+
}
|
|
60
|
+
// add custom data
|
|
61
|
+
to.data.title = 'Hello World'
|
|
62
|
+
// finally return the modified route object
|
|
63
|
+
return to
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
],
|
|
68
|
+
// ...
|
|
69
|
+
})
|
|
70
|
+
```
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Router Transitions
|
|
2
|
+
|
|
3
|
+
Page transitions add a layer of visual polish to your application by animating the change between routes.
|
|
4
|
+
|
|
5
|
+
When the router in Blits navigates to a new page, it automatically applies a subtle _fade-in / fade-out_ transition. Blits also allows to specify custom transitions on a per-route basis.
|
|
6
|
+
|
|
7
|
+
## Defining Route Transitions
|
|
8
|
+
|
|
9
|
+
In the Route object, the `transition` key can be used to specify which transition should be applied when navigating _to_ that route.
|
|
10
|
+
|
|
11
|
+
It accepts a _Transition_ object, with 3 optional keys: `before`, `in`, `out`.
|
|
12
|
+
|
|
13
|
+
### Before
|
|
14
|
+
|
|
15
|
+
The `before` key is used to set a property to a certain value _before_ the page transition starts. The properties are set on the page that is being navigated _to_. For example, it can be used to position the page out of screen by setting the `x` value to a negative value, in order to create a _slide in from the right_ effect. Or the `alpha` property can be set to `0` if you want to create a _fade-in_ effect.
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
const pageTransition = {
|
|
19
|
+
// position the new page outside the screen on the left side
|
|
20
|
+
before: {
|
|
21
|
+
prop: 'x',
|
|
22
|
+
value: -1920
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
In order to set multiple properties to an initial state, an Array of objects can be assigned to the `before` key.
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
const pageTransition = {
|
|
31
|
+
// position the new page outside the screen on the left side
|
|
32
|
+
// and set the alpha to 0
|
|
33
|
+
before: [{
|
|
34
|
+
prop: 'x',
|
|
35
|
+
value: -1920
|
|
36
|
+
},{
|
|
37
|
+
prop: 'alpha',
|
|
38
|
+
value: 0
|
|
39
|
+
}]
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### In
|
|
44
|
+
|
|
45
|
+
The `in`-key is used to define how the new page should transition _into_ the screen, given the initial defaults, in combination with any properties set in the optional `before` key.
|
|
46
|
+
|
|
47
|
+
The transition is defined by a _Transition_ object, consisting of:
|
|
48
|
+
|
|
49
|
+
- `prop` - the property to apply the transition on
|
|
50
|
+
- `value` - the value to transition to
|
|
51
|
+
- `duration` (optional) - the duration of the transition in milliseconds (defaults to `300ms`)
|
|
52
|
+
- `easing` (optional) - the easing function applied to the transition (defaults to `ease-in`)
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
const pageTransition = {
|
|
56
|
+
// position the new page outside the screen on the left side
|
|
57
|
+
before: {
|
|
58
|
+
prop: 'x',
|
|
59
|
+
value: -1920
|
|
60
|
+
},
|
|
61
|
+
// transition the new page from outside, into the screen
|
|
62
|
+
in: {
|
|
63
|
+
prop: 'x',
|
|
64
|
+
value: 0,
|
|
65
|
+
duration: 800,
|
|
66
|
+
easing: 'cubic-bezier(0.20, 1.00, 0.80, 1.00)'
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
In order to transition multiple properties, an Array of objects can be assigned to the `in` key.
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
const pageTransition = {
|
|
75
|
+
// position the new page outside the screen on the left side
|
|
76
|
+
// and set the alpha to 0
|
|
77
|
+
before: [{
|
|
78
|
+
prop: 'x',
|
|
79
|
+
value: -1920
|
|
80
|
+
},{
|
|
81
|
+
prop: 'alpha',
|
|
82
|
+
value: 0
|
|
83
|
+
}],
|
|
84
|
+
// transition the new page from outside, into the screen
|
|
85
|
+
// and transition the alpha from 0 to 1
|
|
86
|
+
in: [{
|
|
87
|
+
prop: 'x',
|
|
88
|
+
value: 0,
|
|
89
|
+
duration: 800,
|
|
90
|
+
easing: 'cubic-bezier(0.20, 1.00, 0.80, 1.00)'
|
|
91
|
+
},{
|
|
92
|
+
prop: 'alpha',
|
|
93
|
+
value: 1,
|
|
94
|
+
duration: 500,
|
|
95
|
+
}]
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Out
|
|
100
|
+
|
|
101
|
+
Finally the `out`-key is used to define how the old page should transition _out of_ the screen.
|
|
102
|
+
|
|
103
|
+
Similar to the `in`-transition, the `out`-transition is defined by a _Transition object_, consisting of:
|
|
104
|
+
|
|
105
|
+
- `prop` - the property to apply the transition on
|
|
106
|
+
- `value` - the value to transition to
|
|
107
|
+
- `duration` (optional) - the duration of the transition in milliseconds (defaults to `300ms`)
|
|
108
|
+
- `easing` (optional) - the easing function applied to the transition (defaults to `ease-in`)
|
|
109
|
+
|
|
110
|
+
And in order to transition multiple properties at the same time during the _out_ transition, an Array of transition objects can be supplied.
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
```js
|
|
114
|
+
const pageTransition = {
|
|
115
|
+
// position the new page outside the screen on the left side
|
|
116
|
+
// and set the alpha to 0
|
|
117
|
+
before: [{
|
|
118
|
+
prop: 'x',
|
|
119
|
+
value: -1920
|
|
120
|
+
},{
|
|
121
|
+
prop: 'alpha',
|
|
122
|
+
value: 0
|
|
123
|
+
}],
|
|
124
|
+
// transition the new page from outside, into the screen
|
|
125
|
+
// and transition the alpha from 0 to 1
|
|
126
|
+
in: [{
|
|
127
|
+
prop: 'x',
|
|
128
|
+
value: 0,
|
|
129
|
+
duration: 800,
|
|
130
|
+
easing: 'cubic-bezier(0.20, 1.00, 0.80, 1.00)'
|
|
131
|
+
},{
|
|
132
|
+
prop: 'alpha',
|
|
133
|
+
value: 1,
|
|
134
|
+
duration: 500,
|
|
135
|
+
}],
|
|
136
|
+
// slide up the old page
|
|
137
|
+
// while rotating
|
|
138
|
+
out: [{
|
|
139
|
+
prop: 'y',
|
|
140
|
+
value: -1080
|
|
141
|
+
},{
|
|
142
|
+
prop: 'rotate',
|
|
143
|
+
value: 720
|
|
144
|
+
}]
|
|
145
|
+
}
|
|
146
|
+
```
|
package/docs/sidebar.json
CHANGED
|
@@ -108,6 +108,19 @@
|
|
|
108
108
|
}
|
|
109
109
|
]
|
|
110
110
|
},
|
|
111
|
+
{
|
|
112
|
+
"text": "Router",
|
|
113
|
+
"items": [
|
|
114
|
+
{
|
|
115
|
+
"text": "Basics",
|
|
116
|
+
"link": "/router/basics"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"text": "Hooks",
|
|
120
|
+
"link": "/router/hooks"
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
},
|
|
111
124
|
{
|
|
112
125
|
"text": "Plugins",
|
|
113
126
|
"items": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightningjs/blits",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.1",
|
|
4
4
|
"description": "Blits: The Lightning 3 App Development Framework",
|
|
5
5
|
"bin": "bin/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@lightningjs/msdf-generator": "^1.1.0",
|
|
53
|
-
"@lightningjs/renderer": "^2.
|
|
53
|
+
"@lightningjs/renderer": "^2.7.1"
|
|
54
54
|
},
|
|
55
55
|
"repository": {
|
|
56
56
|
"type": "git",
|
package/src/component.js
CHANGED
|
@@ -176,6 +176,7 @@ const Component = (name = required('name'), config = required('config')) => {
|
|
|
176
176
|
for (let i = 0; i < watcherkeysLength; i++) {
|
|
177
177
|
let target = this
|
|
178
178
|
let key = watcherkeys[i]
|
|
179
|
+
const watchKey = key
|
|
179
180
|
// when dot notation used, find the nested target
|
|
180
181
|
if (key.indexOf('.') > -1) {
|
|
181
182
|
const keys = key.split('.')
|
|
@@ -190,7 +191,7 @@ const Component = (name = required('name'), config = required('config')) => {
|
|
|
190
191
|
effect((force = false) => {
|
|
191
192
|
const newValue = target[key]
|
|
192
193
|
if (old !== newValue || force === true) {
|
|
193
|
-
this[symbols.watchers][
|
|
194
|
+
this[symbols.watchers][watchKey].apply(this, [newValue, old])
|
|
194
195
|
old = newValue
|
|
195
196
|
}
|
|
196
197
|
})
|
|
@@ -24,9 +24,11 @@ import Settings from '../../settings.js'
|
|
|
24
24
|
|
|
25
25
|
const layoutFn = function (config) {
|
|
26
26
|
let offset = 0
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
27
|
+
const position = config.direction === 'vertical' ? 'y' : 'x'
|
|
28
|
+
const oppositePosition = config.direction === 'vertical' ? 'x' : 'y'
|
|
29
|
+
const oppositeMount = config.direction === 'vertical' ? 'mountX' : 'mountY'
|
|
30
|
+
const dimension = config.direction === 'vertical' ? 'height' : 'width'
|
|
31
|
+
const oppositeDimension = config.direction === 'vertical' ? 'width' : 'height'
|
|
30
32
|
|
|
31
33
|
const children = this.children
|
|
32
34
|
const childrenLength = children.length
|
|
@@ -34,11 +36,11 @@ const layoutFn = function (config) {
|
|
|
34
36
|
const gap = config.gap || 0
|
|
35
37
|
for (let i = 0; i < childrenLength; i++) {
|
|
36
38
|
const node = children[i]
|
|
37
|
-
node[
|
|
39
|
+
node[position] = offset
|
|
38
40
|
// todo: temporary text check, due to 1px width of empty text node
|
|
39
|
-
if (
|
|
41
|
+
if (dimension === 'width') {
|
|
40
42
|
offset += node.width + (node.width !== ('text' in node ? 1 : 0) ? gap : 0)
|
|
41
|
-
} else if (
|
|
43
|
+
} else if (dimension === 'height') {
|
|
42
44
|
offset +=
|
|
43
45
|
'text' in node
|
|
44
46
|
? node.width > 1
|
|
@@ -48,11 +50,25 @@ const layoutFn = function (config) {
|
|
|
48
50
|
? node.height + gap
|
|
49
51
|
: 0
|
|
50
52
|
}
|
|
51
|
-
otherDimension = Math.max(otherDimension, node[
|
|
53
|
+
otherDimension = Math.max(otherDimension, node[oppositeDimension])
|
|
52
54
|
}
|
|
53
55
|
// adjust the size of the layout container
|
|
54
|
-
this[
|
|
55
|
-
this[
|
|
56
|
+
this[dimension] = offset - gap
|
|
57
|
+
this[oppositeDimension] = otherDimension
|
|
58
|
+
|
|
59
|
+
const align = {
|
|
60
|
+
start: 0,
|
|
61
|
+
end: 1,
|
|
62
|
+
center: 0.5,
|
|
63
|
+
}[config['align-items'] || 'start']
|
|
64
|
+
|
|
65
|
+
if (align !== 0) {
|
|
66
|
+
for (let i = 0; i < childrenLength; i++) {
|
|
67
|
+
const node = children[i]
|
|
68
|
+
node[oppositePosition] = otherDimension
|
|
69
|
+
node[oppositeMount] = align
|
|
70
|
+
}
|
|
71
|
+
}
|
|
56
72
|
}
|
|
57
73
|
|
|
58
74
|
const isTransition = (value) => {
|
|
@@ -146,7 +162,7 @@ const propsTransformer = {
|
|
|
146
162
|
set src(v) {
|
|
147
163
|
this.props['src'] = v
|
|
148
164
|
if (this.raw['color'] === undefined) {
|
|
149
|
-
this.props['color'] = 0xffffffff
|
|
165
|
+
this.props['color'] = this.props['src'] ? 0xffffffff : 0x00000000
|
|
150
166
|
}
|
|
151
167
|
},
|
|
152
168
|
set texture(v) {
|
|
@@ -225,7 +241,15 @@ const propsTransformer = {
|
|
|
225
241
|
}
|
|
226
242
|
},
|
|
227
243
|
set show(v) {
|
|
228
|
-
|
|
244
|
+
if (v) {
|
|
245
|
+
this.props['alpha'] = 1
|
|
246
|
+
this.props['width'] = this.raw['w'] || this.raw['width']
|
|
247
|
+
this.props['height'] = this.raw['h'] || this.raw['height']
|
|
248
|
+
} else {
|
|
249
|
+
this.props['alpha'] = 0
|
|
250
|
+
this.props['width'] = 0
|
|
251
|
+
this.props['height'] = 0
|
|
252
|
+
}
|
|
229
253
|
},
|
|
230
254
|
set alpha(v) {
|
|
231
255
|
this.props['alpha'] = v
|
package/src/focus.js
CHANGED
|
@@ -38,6 +38,7 @@ export default {
|
|
|
38
38
|
return focusedComponent
|
|
39
39
|
},
|
|
40
40
|
set(component, event) {
|
|
41
|
+
if (component === focusedComponent) return
|
|
41
42
|
clearTimeout(setFocusTimeout)
|
|
42
43
|
focusedComponent && focusedComponent !== component.parent && focusedComponent.unfocus()
|
|
43
44
|
focusChain.reverse().forEach((cmp) => cmp.unfocus())
|
|
@@ -442,10 +442,18 @@ const generateForLoopCode = function (templateObject, parent) {
|
|
|
442
442
|
|
|
443
443
|
// inject the destroy code in the correct spot
|
|
444
444
|
ctx.renderCode.splice(indexToInjectDestroyCode, 0, ...destroyCode)
|
|
445
|
+
|
|
446
|
+
let effectKey = `${interpolate(result[2], '')}`
|
|
447
|
+
|
|
448
|
+
// Get the last property from nested path
|
|
449
|
+
if (effectKey && effectKey.includes('.')) {
|
|
450
|
+
effectKey = effectKey.match(/[^.]+$/)[0]
|
|
451
|
+
}
|
|
452
|
+
|
|
445
453
|
ctx.renderCode.push(`
|
|
446
454
|
effect(() => {
|
|
447
455
|
forloop${forStartCounter}(${cast(result[2], ':for')}, elms, created${forStartCounter})
|
|
448
|
-
}, '${
|
|
456
|
+
}, '${effectKey}' )
|
|
449
457
|
`)
|
|
450
458
|
|
|
451
459
|
outerScopeEffects.forEach((effect) => {
|
|
@@ -2055,6 +2055,182 @@ test('Generate code for a template with a simple for-loop on an Element', (asser
|
|
|
2055
2055
|
assert.end()
|
|
2056
2056
|
})
|
|
2057
2057
|
|
|
2058
|
+
test('Generate code for a template with a simple for-loop on an Element, Using data from appState plugin', (assert) => {
|
|
2059
|
+
const templateObject = {
|
|
2060
|
+
children: [
|
|
2061
|
+
{
|
|
2062
|
+
[Symbol.for('componentType')]: 'Element',
|
|
2063
|
+
children: [
|
|
2064
|
+
{
|
|
2065
|
+
[Symbol.for('componentType')]: 'Element',
|
|
2066
|
+
':for': 'item in $$appState.list',
|
|
2067
|
+
},
|
|
2068
|
+
],
|
|
2069
|
+
},
|
|
2070
|
+
],
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
const expectedRender = `
|
|
2074
|
+
function anonymous(parent, component, context, components, effect, getRaw) {
|
|
2075
|
+
const elms = []
|
|
2076
|
+
let componentType
|
|
2077
|
+
const rootComponent = component
|
|
2078
|
+
let propData
|
|
2079
|
+
const elementConfig0 = {}
|
|
2080
|
+
elms[0] = this.element({ parent: parent || 'root' }, component)
|
|
2081
|
+
elms[0].populate(elementConfig0)
|
|
2082
|
+
parent = elms[0]
|
|
2083
|
+
const created1 = []
|
|
2084
|
+
const forloop1 = (collection = [], elms, created) => {
|
|
2085
|
+
const rawCollection = getRaw(collection)
|
|
2086
|
+
const keys = new Set()
|
|
2087
|
+
let l = rawCollection.length
|
|
2088
|
+
while (l--) {
|
|
2089
|
+
const item = rawCollection[l]
|
|
2090
|
+
const index = l
|
|
2091
|
+
keys.add('' + l)
|
|
2092
|
+
}
|
|
2093
|
+
let i = created.length
|
|
2094
|
+
while (i--) {
|
|
2095
|
+
if (keys.has(created[i]) === false) {
|
|
2096
|
+
const key = created[i]
|
|
2097
|
+
elms[1][key] && elms[1][key].destroy()
|
|
2098
|
+
delete elms[1][key]
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
created.length = 0
|
|
2102
|
+
const length = rawCollection.length
|
|
2103
|
+
for (let __index = 0; __index < length; __index++) {
|
|
2104
|
+
const scope = Object.create(component)
|
|
2105
|
+
parent = elms[0]
|
|
2106
|
+
scope['index'] = __index
|
|
2107
|
+
scope['item'] = rawCollection[__index]
|
|
2108
|
+
scope['key'] = '' + __index
|
|
2109
|
+
created.push(scope.key)
|
|
2110
|
+
parent = elms[0]
|
|
2111
|
+
if (elms[1] === undefined) {
|
|
2112
|
+
elms[1] = {}
|
|
2113
|
+
}
|
|
2114
|
+
const elementConfig1 = {}
|
|
2115
|
+
if (elms[1][scope.key] === undefined) {
|
|
2116
|
+
elms[1][scope.key] = this.element({ parent: parent || 'root' }, component)
|
|
2117
|
+
}
|
|
2118
|
+
if (elms[1][scope.key].nodeId === undefined) {
|
|
2119
|
+
elms[1][scope.key].populate(elementConfig1)
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
effect(() => {
|
|
2124
|
+
forloop1(component.$appState.list, elms, created1)
|
|
2125
|
+
}, 'list')
|
|
2126
|
+
return elms
|
|
2127
|
+
}
|
|
2128
|
+
`
|
|
2129
|
+
|
|
2130
|
+
const actual = generator.call(scope, templateObject)
|
|
2131
|
+
|
|
2132
|
+
assert.equal(
|
|
2133
|
+
normalize(actual.render.toString()),
|
|
2134
|
+
normalize(expectedRender),
|
|
2135
|
+
'Generator should return a render function with the correct code using data from appState plugin'
|
|
2136
|
+
)
|
|
2137
|
+
|
|
2138
|
+
assert.ok(
|
|
2139
|
+
Array.isArray(actual.effects) && actual.effects.length === 0,
|
|
2140
|
+
'Generator should return an empty effect array'
|
|
2141
|
+
)
|
|
2142
|
+
|
|
2143
|
+
assert.end()
|
|
2144
|
+
})
|
|
2145
|
+
|
|
2146
|
+
test('Generate code for a template with a simple for-loop on an Element, Using data from nested state property', (assert) => {
|
|
2147
|
+
const templateObject = {
|
|
2148
|
+
children: [
|
|
2149
|
+
{
|
|
2150
|
+
[Symbol.for('componentType')]: 'Element',
|
|
2151
|
+
children: [
|
|
2152
|
+
{
|
|
2153
|
+
[Symbol.for('componentType')]: 'Element',
|
|
2154
|
+
':for': 'item in $content.data',
|
|
2155
|
+
},
|
|
2156
|
+
],
|
|
2157
|
+
},
|
|
2158
|
+
],
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
const expectedRender = `
|
|
2162
|
+
function anonymous(parent, component, context, components, effect, getRaw) {
|
|
2163
|
+
const elms = []
|
|
2164
|
+
let componentType
|
|
2165
|
+
const rootComponent = component
|
|
2166
|
+
let propData
|
|
2167
|
+
const elementConfig0 = {}
|
|
2168
|
+
elms[0] = this.element({ parent: parent || 'root' }, component)
|
|
2169
|
+
elms[0].populate(elementConfig0)
|
|
2170
|
+
parent = elms[0]
|
|
2171
|
+
const created1 = []
|
|
2172
|
+
const forloop1 = (collection = [], elms, created) => {
|
|
2173
|
+
const rawCollection = getRaw(collection)
|
|
2174
|
+
const keys = new Set()
|
|
2175
|
+
let l = rawCollection.length
|
|
2176
|
+
while (l--) {
|
|
2177
|
+
const item = rawCollection[l]
|
|
2178
|
+
const index = l
|
|
2179
|
+
keys.add('' + l)
|
|
2180
|
+
}
|
|
2181
|
+
let i = created.length
|
|
2182
|
+
while (i--) {
|
|
2183
|
+
if (keys.has(created[i]) === false) {
|
|
2184
|
+
const key = created[i]
|
|
2185
|
+
elms[1][key] && elms[1][key].destroy()
|
|
2186
|
+
delete elms[1][key]
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
created.length = 0
|
|
2190
|
+
const length = rawCollection.length
|
|
2191
|
+
for (let __index = 0; __index < length; __index++) {
|
|
2192
|
+
const scope = Object.create(component)
|
|
2193
|
+
parent = elms[0]
|
|
2194
|
+
scope['index'] = __index
|
|
2195
|
+
scope['item'] = rawCollection[__index]
|
|
2196
|
+
scope['key'] = '' + __index
|
|
2197
|
+
created.push(scope.key)
|
|
2198
|
+
parent = elms[0]
|
|
2199
|
+
if (elms[1] === undefined) {
|
|
2200
|
+
elms[1] = {}
|
|
2201
|
+
}
|
|
2202
|
+
const elementConfig1 = {}
|
|
2203
|
+
if (elms[1][scope.key] === undefined) {
|
|
2204
|
+
elms[1][scope.key] = this.element({ parent: parent || 'root' }, component)
|
|
2205
|
+
}
|
|
2206
|
+
if (elms[1][scope.key].nodeId === undefined) {
|
|
2207
|
+
elms[1][scope.key].populate(elementConfig1)
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
effect(() => {
|
|
2212
|
+
forloop1(component.content.data, elms, created1)
|
|
2213
|
+
}, 'data')
|
|
2214
|
+
return elms
|
|
2215
|
+
}
|
|
2216
|
+
`
|
|
2217
|
+
|
|
2218
|
+
const actual = generator.call(scope, templateObject)
|
|
2219
|
+
|
|
2220
|
+
assert.equal(
|
|
2221
|
+
normalize(actual.render.toString()),
|
|
2222
|
+
normalize(expectedRender),
|
|
2223
|
+
'Generator should return a render function with the correct code using data from nested state property'
|
|
2224
|
+
)
|
|
2225
|
+
|
|
2226
|
+
assert.ok(
|
|
2227
|
+
Array.isArray(actual.effects) && actual.effects.length === 0,
|
|
2228
|
+
'Generator should return an empty effect array'
|
|
2229
|
+
)
|
|
2230
|
+
|
|
2231
|
+
assert.end()
|
|
2232
|
+
})
|
|
2233
|
+
|
|
2058
2234
|
test('Generate code for a template with a simple for-loop on an Element with a custom index key', (assert) => {
|
|
2059
2235
|
const templateObject = {
|
|
2060
2236
|
children: [
|